Going down to generic type
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 several 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 cannot distinguish FTPFileManager
before IFileManager<IFile>
, but I wonder if it can distinguish between FTPFileManager
before IFileManager<FTPFile>
. In both cases, there is no compilation error, but it throws an error at runtime.
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
}
}
source to share
One way to do this is to use the following template:
public interface IFile { }
public class FTPFile : IFile { }
public interface IFileManager
{
void SaveFile(IFile file);
bool Accepts(IFile file);
}
public abstract class FileManager<TFile>
: IFileManager
where TFile : IFile
{
protected abstract void SaveFile(TFile file);
public void SaveFile(IFile file)
{
var cast = (TFile)file; // will throw a compile exception if wrong one
this.SaveFile(cast);
}
public bool Accepts(IFile file)
{
return file is TFile;
}
}
internal class FTPFileManager : FileManager<FTPFile>
{
protected sealed override void SaveFile(FTPFile file)
{
Console.WriteLine("Saved file");
}
}
public static IFileManager GetFileManager(IFile file)
{
var managers = new List<IFileManager> { new FTPFileManager() };
return managers.Single(manager => manager.Accepts(file));
}
source to share
You could distinguish FTPFileManager
before IFileManager<IFile>
if you IFileManager
were covariant, that is, declared as:
public interface IFileManager<out T> where T : IFile
However, this won't compile because it IFileManager
has T
one of its methods as an input parameter. For an interface to be covariant in T
, it T
only needs to have a return type.
This makes sense in your situation, because FTPFileManager
an instance must be provided FTPFile
. If you could write:
var ftpManager = new FtpFileManager();
IFileManager<IFile> fileManager = ftpManager;
fileManager.SaveFile(new SomeOtherFileType());
Then the FTP file manager will wait FTPFile
, but something else will be transferred instead. For this reason, the second line refuses to compile.
Note that this also indicates a decrease in the level of risk (or at least a decrease, without being clear why you are doing this). By writing:
return (IFileManager<IFile>)new FTPFileManager();
instead
return new FTPFileManager();
you turned a compile-time error at runtime.
source to share