向下转型为通用类型

时间:2014-11-21 12:25:24

标签: c#

我有以下课程:

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
    }
}

3 个答案:

答案 0 :(得分:3)

如果FTPFileManager是协变的,您可以将IFileManager<IFile>投射到IFileManager,即声明为:

public interface IFileManager<out T> where T : IFile

但是,这不会编译,因为IFileManagerT作为其某个方法的输入参数。要使接口在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();                 
        }
    }

尝试以上。它会工作。