【问题标题】:How do I create a generic property where the type is determined by the compiler?如何创建类型由编译器确定的泛型属性?
【发布时间】:2014-10-18 17:21:01
【问题描述】:

我已经搜索过,但找不到我需要做的事情的答案。 我需要有两个从基类继承的类,具有通用属性。属性的类型应由编译器确定,具体取决于类的类型。 现在,我有这个:

public abstract class AbstractClass<T>
{
    public abstract List<T> Items { get; set; }
}

public class ChildClass1 : AbstractClass<MyType1>
{
    public override List<MyType1> Items { get; set; }
}

public class ChildClass2 : AbstractClass<MyType2>
{
    public override List<MyType2> Items { get; set; }
}

但是当我创建一个新的 AbstractClass 对象时,我仍然需要指定我想要的类型:

AbstractClass<MyType1> child1 = new ChildClass1();

如何做才能在创建实例时不必指定属性的类型?

例如:

AbstractClass child1 = new ChildClass1();

【问题讨论】:

  • 您希望编译器如何推断抽象类的泛型类型T
  • 该代码无法编译,您没有覆盖抽象属性。
  • 糟糕,我的意思是在 Items 属性中添加覆盖
  • 如果您不添加任何附加功能,您甚至不必声明Items 属性摘要,也不必覆盖它们。简单的public List&lt;T&gt; Items {get; set;} 就足够了

标签: c# .net generics properties abstract


【解决方案1】:

让您的AbstractClass&lt;T&gt; 已经从更通用的AbstractClass 继承,您可以在编译时不知道类型 T 的情况下工作。在那里,您可以使用完全不依赖于 T 的常用方法。此时您将无法直接访问基于 T 的任何项目。

public abstract class AbstractClass
{
   public abstract void MethodOnAbstractClass();
}

public abstract class AbstractClass<T>: AbstractClass
{
    public abstract List<T> Items { get; set; }
}

但是,您已经可以定义要在特定类中实现的抽象方法,这些类可能依赖于来自 T 的信息,如下所示:

public abstract class AbstractClass
{
   public abstract IList GetItems(); //has to be implemented later on
}

public abstract class AbstractClass<T>: AbstractClass
{
    public List<T> Items { get; set; }
    public override IList GetItems() { return Items; }  //Items only visible as objects.

    public AbstractClass() 
    {
        Items = new List<T>(); //Make sure example further down is working.
    }
}

class ChildClass1 : AbstractClass<MyType1> { }
class ChildClass2 : AbstractClass<MyType2> { }

这里的缺点是,与 Items 属性相比,您不会在编译时知道 GetItems() 返回的 IList 中包含的类型,而只能在运行时知道。但它允许你做这样的事情:

AbstractClass child;

child = new ChildClass1();
int count1 = child.GetItems().Count; 

child = new ChildClass2();
int count2 = child.GetItems().Count;

【讨论】:

  • 将 AbstractClass 标记为 abstract
【解决方案2】:

好的,正如你所发现的,你不能那样做。

但是为什么呢?

因为AbstractClass 不是类型,而AbstractClass&lt;string&gt; 是类型。 “为什么?”这是“因为它就是这样工作的”。

你必须在某个阶段告诉编译器什么类型正在替换T。你定义类的方式你说它必须在声明对类的引用时预先完成。

你可以尝试其他的东西,但你永远无法在不指定类型的情况下使用该属性(除非你想完全删除类型安全并在任何地方都有object,但我认为没有人会这样做推荐!)

你可以声明一个接口

interface GenericItems
{
    T GetItems<T>();
}

并有一个 AbstractBaseClass 来实现它,但是没有什么能阻止有人要求与实现该对象的类型 T 不同的类型,所以这也不是一个解决方案

最好的方法是你设计它的方式,它只能这样工作,因为编译器需要知道你真正想要哪种类型的 AbstractClass。它无法从作业的右侧推断它。

但是,如果您想以多态方式处理各种 AbstractClass 对象AND,您不关心类型化的 Items 属性,那么您可以创建一个 AbstractBaseClass,它不是t 泛型类型。 您将所有多态位放在“AbstractBaseClass”中,然后让AbstractClass&lt;T&gt; 从 AbstractBaseClass 继承。

例如

abstract class AbstractBaseClass
{
    abstract void DoThis();
    abstract void DoThat();
}

abstract class AbstractClass<T>
    : AbstractBaseClass
{
    override void DoThis(){...}
    override void DoThat(){...}
    public T Items{...}
}

那你就可以了

AbstractBaseClass abstractBaseClass = new ChildClass1();
abstractBaseClass.DoThis();

不能这样做

AbstractBaseClass abstractBaseClass = new ChildClass1();
abstractBaseClass.Items.Count;  // Compiler error, unknown property or method Items

【讨论】:

  • 但是,您可以在您的AbstractBaseClass 中添加一个public abstract IList GetItems();,然后在您的AbstractClass&lt;T&gt; 中添加public override IList GetItems() { return Items; },这样您就可以使用代码abstractBaseClass.GetItems().Count 这不是我会选择设计它,因为我更喜欢知道我的收藏中的对象类型,但它可以做到。
  • 是的,你可以但我宁愿DoThis抽象方法之一会返回计数,如果这是需要的话,或者做计数的事情需要。我知道你是说你不会那样做,但在那样做之前,我会更多深入类设计的兔子洞。
【解决方案3】:

以这个基类为例:

public abstract class Base<T>
{
    public abstract List<T> TheList { get; set; }
}

你可以创建一个子类:

public class MyClass : Base<string>
{
    public override List<string> TheList
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }
}

您的子属性仍然需要指定属性类型。您必须明确,不能使用var 或其他东西。

编译器无法判断您想使用泛型参数作为属性的类型。如果您使用的是 Visual Studio,并且有一个空白的子类,它将帮助您,但最终该语言仍将要求您提供每个属性的类型。

这将创建您在上面看到的 MyClass 代码。

当您实例化该类时,您不必提供通用参数,因为您已经知道 MyClass 会将 string 作为通用参数传递给 Base

var mc = new MyClass();
//mc is a MyClass, which inherits from Base<string>

但同样,您必须提前告诉全班所有内容的类型。

但是,如果您不想创建子类,则可以直接使用基类。从基类中删除“摘要”后,请使用:

var b1 = new Base<string>;
List<string> b1List = b1.TheList; //TheList is now a string list

是的,您必须在创建新对象时设置类型,但您不必创建子类。

无论哪种方式,您都必须在某处定义类型。如果您想在子类或对象实例化中执行此操作,这取决于您。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-01
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    • 2020-08-06
    • 2011-07-17
    • 1970-01-01
    • 2023-03-26
    相关资源
    最近更新 更多