Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET 4.0 Cast Generic Interfaces

I have two Interfaces, one of them is a generic one, allowing only Types that derive from the second Interface. They look like this:

public interface IProvider<T> where T : IContent
{
    T getContent(int i);
    void addContent(T content);
}
public interface IContent
{
    string whatIAm();
}

Of course my real Interfaces are more complex but it is enought to show what my problem is. Now i have for each interface a concrete class:

public class Provider : IProvider<FileContent> 
{
    public FileContent getContent(int i)
    {
        return null;
    }
    public void addContent(FileContent content)
    {
    }
}

public class FileContent : IContent{
    public string whatIAm(){
        return "FileContent";
    }
}

And in my code i want to work with the reference type "IProvider" but the cast goes wrong... Please look at this example:

 static void Main(string[] args)
    {
        Provider p = new Provider(); //works
        IProvider<FileContent> pp = p as IProvider<FileContent>; //also works
        IProvider<IContent> ppp = pp as IProvider<IContent>; //fails :(
    }

ppp is always null. What do i have to change that this cast is working? Thanks in advance.

like image 301
user1531730 Avatar asked Dec 26 '22 21:12

user1531730


2 Answers

The type argument must match exactly. IProvider<IContent> is a different type than IProvider<FileContent>, there is no inheritance between them.

Imagine you have an IProvider<IContent> ppp  from your IProvider<FileContent> and a developer tries ppp.addContent(someOtherContentThatIsNoFileContent). That statement is valid for IProvider<IContent>, but it would break type safety, so not allowing such a conversion is the right thing to do. 

Covariance and Contravariance for generic type parameters allow something like this under certain circumstances, but since your interface uses the type parameter both as in- and output parameter, this won't apply to it the way it is declared right now.

EDIT: Look at IEnumerable's definition:

public interface IEnumerable<out T> 

So you know IEnumerable uses T only as output parameter (you can't add items, only enumerate them), and the out keyword specifies that T is covariant. So you can do

IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;

If you want to do this, you would have to remove the add method from your interface. Same applies for input parameters and the in keyword on generic type parameters. 

Your interface would then look like this:

public interface IProvider<out T> where T : IContent
{
    T getContent(int i);
}
like image 194
Botz3000 Avatar answered Jan 21 '23 22:01

Botz3000


This isnt the way generic work in C#. A generic of IProvider<FileContent> is not a subtype of IProvider<IContent>.

like image 24
Not loved Avatar answered Jan 21 '23 22:01

Not loved