我有以下课程:
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) { }
}
我的项目中定义了多种文件类型和管理器,但我试图保持简单。在我的工厂类中,我有一个像下面这样的方法,它不能将FTPFileManager
强制转换为IFileManager<IFile>
,但有趣的是它可以将FTPFileManager
强制转换为IFileManager<FTPFile>
。在这两种情况下都没有编译错误,但在运行时它会抛出错误。
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
}
}
答案 0 :(得分:3)
如果FTPFileManager
是协变的,您可以将IFileManager<IFile>
投射到IFileManager
,即声明为:
public interface IFileManager<out T> where T : IFile
但是,这不会编译,因为IFileManager
将T
作为其某个方法的输入参数。要使接口在T
中具有协变性,它必须仅将T
作为 return 参数的类型。
这在您的情况下是有意义的,因为FTPFileManager
需要被赋予FTPFile
的实例。如果你能写:
var ftpManager = new FtpFileManager();
IFileManager<IFile> fileManager = ftpManager;
fileManager.SaveFile(new SomeOtherFileType());
然后FTP文件管理器将期待FTPFile
,而是传递其他东西。因此,第二行拒绝编译。
请注意,这也会突显降低风险(或者,至少是向下倾斜,而不是非常清楚为什么要这样做)。写下:
return (IFileManager<IFile>)new FTPFileManager();
而不是
return new FTPFileManager();
您将编译时错误转换为运行时错误。
答案 1 :(得分:1)
解决此问题的一种方法是使用以下模式:
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));
}
答案 2 :(得分:0)
internal class FTPFileManager : IFileManager<IFile>
{
public void SaveFile(IFile file) { }
}
public class FileManagerFactory
{
public static IFileManager<IFile> GetFileManager(IFile file)
{
if (file is FTPFile)
return new FTPFileManager();
}
}
尝试以上。它会工作。