简体   繁体   中英

C# casting to object in interface generic

i have a problem that I do not understand about casting in interfaces with generic (probably because covariance and contravariance are not completly clear to me at the moment).

I have an Interface where i define a getter and setter that should accept anything in a typed way (no object as type) Eg :

public interface IDummy <T>
{
     int SomeCommonMethod() ;
     T   Anything { get; set; }
}

Now i define some concretes implementation of interface defined before.

public class MyStrObj : IDummy <string>
{
     private string _stirngVal = string.Empty ;

     public int SomeCommonMethod() 
     {
         return 0 ;
     }

     public string Anything 
     { 
         get { return _stirngVal  ; } 
         set { _stirngVal = value ; }
     }
}

public class MyFileObj : IDummy <File>
{
     private File _file = null ;

     public int SomeCommonMethod() 
     {
         return 0 ;
     }

     public File Anything 
     { 
         get { return  _file ; } 
         set { _file = value ; }
     }
}

At time all works as expected, but now when try to use this two object their behaviour start becoming confusing for me.

I try to define an object that should be able to consume both previous classes (no matter which type they has in their generics, what matter is that they are IDummy).

public class Consumer
{
     public static void Consume ( IDummy<object> obj )
     {
          //SOME CODE HERE.
     }
}

Now if i try this code :

MyStrObj obj = new MyStrObj () ;
Consumer.Consume ( obj ) ;

then compiler notice to me that there are some invalid parameters over Consume method calling (obj sure), but there is not an implicit cast here?

If i try this way instead :

MyStrObj obj = new MyStrObj () ;
Consumer.Consume ( (IDummy<object>)obj ) ;

compiler seems to work as I suppose it should (at time I have not tested if the two calls are equivalent).

Thanks in advance for anyone that can help me to understand this behaviour, and sorry for my english (is not my language).

Your IDummy<T> is not covariant. That is why the implicit conversion of such does not work. If it were covariant, the conversion from more specific generic type to the more general one would work. However in your example you cannot make your interface covariant IDummy<out T> , since it has a property setter with your generic parameter.

The method in your Consumer class needs to be generic.

public class Consumer
{
     public static void Consume<T> ( IDummy<T> obj )
     {
          //SOME CODE HERE.
     }
}

Then you can do

var foo = new MyStrObj();
foo.Anything = "Hello";
Consumer.Consume(foo);

In order to achieve what you described using covariance you need to amend your interface to make it covariant.

Covariance allows you to assign more specific types to less specific. This is possible however only for the classes that only return your objects of the template type.

Hence in your interface you need to remove the setter ant mark the T as covariant using the keyword out . You can keep the setter on the classes that implement it though. So your interface would have to look like this:

public interface IDummy<out T>
{
    int SomeCommonMethod();
    T Anything { get; }
}

Your MyStrObj , MyFileObj and Consumer classes can stay as they are. After the change you can utilize the covariance when calling the Consume method.

    MyStrObj obj = new MyStrObj();
    obj.Anything = "My string";
    Consumer.Consume(obj);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM