在接口泛型中将C#转换为对象

3

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

我定义了一个接口,其中我定义了一个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 ) ;

编译器提示我在调用Consume方法时有一些无效参数(obj确定),但这里没有隐式转换?

如果我尝试这种方式:

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

编译器看起来按照我预期的方式工作(此时我还没有测试这两个调用是否等效)。
感谢任何可以帮助我理解这种行为的人,并对我的英语表示歉意(这不是我的母语)。
3个回答

4
您的 IDummy<T> 不是协变的。因此,这就是为什么这样的隐式转换不起作用的原因。如果它是协变的,从更特定的泛型类型到更一般的类型的转换将起作用。但是在您的示例中,您不能使您的接口协变 IDummy<out T>,因为它具有带有泛型参数的属性设置器。

谢谢,这解释(和简化)了很多情况。在我的上面的例子(也是真实情况),泛型可能只是对象,因此设置器并不是必需的(我想设置的内容在对象引用内部,而不在对象引用本身)。 - Skary

2

你的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);

你可能想提到IDummy<string>不是从IDummy<object>派生的,我们真的不应该从一个转换为另一个。 - Fun Mun Pieng
public class Consumer { IDummy<T> myObj; //why does this not work? public static void Consume<T> ( IDummy<T> obj ) { myObj=obj; //why does this not work? } } - Ankit Khanal
@AnkitKhanal - 因为要有一个带有通用类型的字段,类也必须是通用的。否则,在字段声明中没有可绑定到类型参数的类型可用。 - Andrew Cooper

-1
为了使用协变实现您所描述的功能,您需要修改接口以使其具有协变性。
协变允许您将更具体的类型分配给不太具体的类型。但是,这仅适用于仅返回模板类型对象的类。
因此,在您的接口中,您需要删除setter并使用关键字out将T标记为协变。您可以在实现它的类上保留setter。因此,您的接口应如下所示:
public interface IDummy<out T>
{
    int SomeCommonMethod();
    T Anything { get; }
}

你的 MyStrObjMyFileObjConsumer 类可以保持不变。在更改后,你可以在调用 Consume 方法时利用协变性。

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

2
+0:不会起作用 - 接口中存在读/写属性。 - Alexei Levenkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接