c#转换为接口泛型中的对象

本文关键字:对象 泛型 接口 转换 | 更新日期: 2023-09-27 18:04:47

我有一个问题,我不理解在泛型接口中的强制转换(可能是因为协方差和逆变对我来说目前还不完全清楚)。

我有一个接口,我定义了一个getter和setter应该接受任何类型的方式(没有对象作为类型)如:

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

现在我定义一些之前定义的接口的具体实现。

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

起初一切都如预期的那样工作,但是现在当我尝试使用这两个对象时,它们的行为开始让我感到困惑。

我尝试定义一个对象,它应该能够使用前面的两个类(不管它们的泛型是哪种类型,重要的是它们是IDummy)。

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

现在如果我尝试这个代码:

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

然后编译器通知我,有一些无效的参数超过消费方法调用(obj确定),但这里没有隐式强制转换?

如果我尝试这样做:

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

编译器似乎像我想象的那样工作(有时我没有测试这两个调用是否等效)。

提前感谢任何可以帮助我理解这种行为的人,并为我的英语(不是我的语言)感到抱歉。

c#转换为接口泛型中的对象

您的IDummy<T>不是协变的。这就是为什么这种隐性转换不起作用的原因。如果它是协变的,那么从更具体的泛型到更一般的类型的转换就会有效。然而,在你的例子中,你不能让你的接口协变IDummy<out T>,因为它有一个属性setter与你的通用参数。

您的Consumer类中的方法需要是通用的。

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

然后你可以做

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

为了实现您所描述的使用协方差,您需要修改您的接口以使其协变。

协方差允许您将更特定的类型分配给更不特定的类型。但是,这只适用于只返回模板类型对象的类。

因此,在接口中,您需要删除setter并使用关键字out将T标记为协变。不过,你可以在实现它的类上保留setter。所以你的界面应该是这样的:

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

你的MyStrObj, MyFileObjConsumer类可以保持原样。更改之后,您可以在调用Consume方法时利用协方差。

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