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

      

+3


source to share


3 answers


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

      

+1


source


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.

+3


source


 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();                 
        }
    }

      

Try the above. This will work.

0


source







All Articles