I have the following classes:
public interface IFile { }
public class File : IFile { }
public class FTPFile : File { }
public interface IFileManager<T> where T : IFile
{
void SaveFile(T file);
}
internal class FTPFileManager : IFileManager<FTPFile>
{
public void SaveFile(FTPFile file) { }
}
I have multiple file types and managers defined in my project but I tried to keep it simple. In my factory class, I have a method like the following where it can't cast FTPFileManager to IFileManager<IFile> but interestingly it can cast FTPFileManager to IFileManager<FTPFile>. In both cases there is no compile error but in runtime it throws error.
public class FileManagerFactory
{
public static IFileManager<IFile> GetFileManager(IFile file)
{
if (file is FTPFile)
return (IFileManager<IFile>)new FTPFileManager(); //No compile error but in runtime it throws casting error
}
}
You would be able to cast FTPFileManager to IFileManager<IFile> if IFileManager were covariant, i.e. declared as:
public interface IFileManager<out T> where T : IFile
However, this won't compile because IFileManager has T as an input parameter to one of its methods. For an interface to be covariant in T, it must have T only as a type of return parameters.
This makes sense in your situation, because FTPFileManager needs to be given an instance of FTPFile. If you could write:
var ftpManager = new FtpFileManager();
IFileManager<IFile> fileManager = ftpManager;
fileManager.SaveFile(new SomeOtherFileType());
Then the FTP file manager would be expecting an FTPFile but instead be passed something else. For that reason, the second line refuses to compile.
Note that this also highlights a risk downcasting (or, at least, downcasting without being very clear on why you're doing it). By writing:
return (IFileManager<IFile>)new FTPFileManager();
instead of
return new FTPFileManager();
you turned a compile-time error into a runtime one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With