繁体   English   中英

如何在编译器确定类型的地方创建通用属性?

[英]How do I create a generic property where the type is determined by the compiler?

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

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();

让您的AbstractClass<T>已经从一个更通用的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不是类型,所以AbstractClass<string>是类型。 “为什么?” 这就是“因为这就是它的工作方式”。

您必须在某个阶段告诉编译器哪种类型正在替换T 声明对类的引用时,必须先完成定义类的方法。

您可以尝试其他方法,但是如果不指定类型就永远无法使用该属性(除非您要完全删除类型安全性并且到处都有object ,但是我认为没有人会建议这样做!)

您可以声明一个接口

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

并具有实现该目标的AbstractBaseClass ,但是没有什么可以阻止某人要求与实现该对象的类型T不同的类型的,所以实际上这也不是解决方案

最好的方法是设计它的方法,它只能这样工作,因为编译器需要知道您实际需要哪种类型的AbstractClass。 它无法从作业的右侧进行推断。

但是,如果你想多态治疗各种抽象类的对象你不关心类型的Items属性,当你做到这一点,那么你可以创建一个AbstractBaseClass不是一个泛型类型。 您将所有多态位放在“ AbstractBaseClass”中,然后让AbstractClass<T>从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

以这个基类为例:

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

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

无论哪种方式,您都必须在某个地方定义类型。 是否要在子类或对象实例中完成取决于您。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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