【问题标题】:Can I pass T to a class constructor我可以将 T 传递给类构造函数吗
【发布时间】:2011-09-21 18:56:02
【问题描述】:

我有一门课,例如:

public MyClass
{
   public myEnumType Status {get;set;}
   public DataTable Result{get;set;}
}

因为 DataTables 很烂,我想实现面向对象的方法。但是我有现有的代码,例如:

public interface IData
{
  MyClass AddData(int i);
  MyClass GetData(string Tablename);
}

我想使用这个接口,但我不想返回 DataTable,而是想返回某种类型的对象,例如/Person 类。

我确实想过创建一个继承 MyClass 的新类,如下所示:

public MyInheritedClass<T> : MyClass
{
  public new T Result{get;set;}
}

但是,当您从实现接口的类中获取数据时,您必须将方法的结果从 MyClass 转换为 MyInheritedClass。所以我想知道是否有一种方法可以使用现有的 MyClass 来放置一个传递泛型类型的构造函数,所以我最终得到了类似的东西

public MyClass
{
   public MyClass(T MyObjectOrientatedClass)
   {
      MyOOClass = MyObjectOrientatedClass;
   }
   public myEnumType Status {get;set;}
   public DataTable Result{get;set;}
   public T MyOOClass {get;set;}
}

【问题讨论】:

  • 如果您希望类 MyClass 具有泛型类型的属性,则它必须是泛型的

标签: c# .net generics c#-4.0 .net-4.0


【解决方案1】:

在 C# 中,表达式的类型在编译时由其组成部分的类型确定。这意味着像您的上一个示例一样(仅通过知道类的类型就无法知道属性的类型)无法工作。

想象一下,如果该类定义已编译。那么你就有这个问题了:

// What would you use as the type of this variable?
??? val = GetMyClassFromSomewhere().MyOOClass;

当然你可以使用object,但是你对属性值一无所知,所以你必须先进行转换,然后才能对它做任何事情。


解决您最初的问题,通过从您现有的IData 接口派生一个新的通用接口,可以(有一些粗糙的边缘)以兼容的方式扩展您现有的类型:

public interface IData<T> : IData
{
    new MyInheritedClass<T> AddData(int i);
    new MyInheritedClass<T> GetData(string Tablename);
}

【讨论】:

  • 通过在 IData 中指定 new 关键字,编译器是否仍假定您已实现 IData 方法,如果 IData 添加了新方法,则 IData 会失败,因为并非所有方法都有实施了吗?
  • : IData 表示IData 的所有成员都被继承。 new 关键字允许添加与IData 具有相同签名的新函数(并隐藏它们)。如果您实现IData&lt;T&gt;,您还必须显式实现原始IData 方法(可能只需调用IData&lt;T&gt; 方法)。这就是我所说的“粗糙边缘”。
  • 哦,好吧,我确实走了那条路,但我只是认为这是浪费代码,但是如果显式方法只返回 IData 可能还不错
【解决方案2】:

如果没有在尖括号中声明,您不能引用泛型类型参数,因此您必须有 IData&lt;T&gt; 和/或 MyClass&lt;T&gt; 类型,在这种情况下您不需要将其指定为构造函数的参数。

如果您无法更改这些类型,您仍然可以使用 动态调度 来避免显式转换。重载具有不同子类型的方法并使用动态表达式将方法解析推迟到运行时,它会自动为您转换为子类型:

void DoSomethingWith(Person p) { ... }
void DoSomethingWith(AnotherClass x) { ... }

void DomSomtheingWithMyInheritedClass(MyClass x)
{
   DoSomethingWith((dynamic) x.Result);
}

【讨论】:

  • 问题出在生产中,因此不能将 IData 更改为 IData 或将 MyClass 更改为 MyClass 除非 T 不是必需的,以便旧代码可以编译并且新代码可以使用 T
  • 我在回答中添加了另一个建议。
  • 我没有想到动态的,谢谢,但我想要这个强类型
【解决方案3】:

为什么不创建一个类似的通用接口

public interface IData<T>
{
    T AddData(int i);
    T GetData(string Tablename);
}

我们甚至可以有一个通用的实现:

public class MyGenericClass<T> : IData<T>
{
} 

【讨论】:

  • IData 和 MyClass 在很多地方都使用过,所以除非 T 不是必需品,并且旧代码将通过指定 T 来编译它会起作用。
【解决方案4】:

虽然这是一个有点可怕的想法,但只要你想放入 T 中的内容可以装箱到 MyClass 中,你就可以编写一个类似的方法......

public T GetResult<T>() where T : MyClass
{
    return (T)Result;
}

相当恶心,但它可以让你“封装”演员表。但实际上,我们正在谈论...之间的区别。

var result = (DerivedMyClass)obj.Result;

...和...

var result = obj.GetResult<DerivedMyClass>();

我不确定我是否认为其中一个一定比另一个更好,并且可能会坚持使用演员表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-23
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 2018-04-27
    • 1970-01-01
    • 2010-10-21
    相关资源
    最近更新 更多