为什么要使用&#34; List <t>其中T:SomeType&#34;如果你知道这种类型

时间:2017-11-03 08:52:54

标签: c# generics

我试图理解通用约束。可悲的是,我现在没有理由使用它们,但我会尽力提出情况,以便我可以玩它并了解它们将如何/何时提供帮助。

我遇到了一个问题。对我来说,这两个是相同的

public List<T> Get<T>() where T : DemoClassTwo
public List<DemoClassTwo> Get()

两者都会返回List,并且必须是DemoClassTwo类型,因此我不明白为什么我会使用该约束的方法。

一些实际的代码来演示这个

static void Main(string[] args)
{
    var dco = new DemoClassOne();
    dco.Get().ForEach((a) => { Console.WriteLine(a.Id); });
    dco.Get<DemoClassTwo>().ForEach((a) => { Console.WriteLine(a.Id); });
    Console.ReadKey();
}

支持班级

public class DemoClassOne
{
    public List<T> Get<T>() where T : DemoClassTwo, new()
    {
        var result = new List<T>();
        var t = new T();
        t.Id = 1;
        result.Add(t);
        return result;
    }

    public List<DemoClassTwo> Get()
    {
        var result = new List<DemoClassTwo>();
        var d = new DemoClassTwo();
        d.Id = 1;
        result.Add(d);
        return result;    //Code here is pretty much identical to the other Get method
    }
}

public class DemoClassTwo
{
    public int Id { get; set; }
    public DemoClassTwo()
    {}
}

我是否刚刚发现2 相同的情况,或者我在工作方式上遗漏了什么。

这个问题类似于In C#, why use Where T : ConcreteClass?,但不同之处在于我没有问为什么要使用它,而是为什么它不同。

4 个答案:

答案 0 :(得分:5)

  

我不知道为什么我会使用带有该约束的方法。

然后就是不要。

当您希望相同的代码在不同的类型上运行时使用泛型,并且当您想要约束可以使用该泛型方法的类型时使用约束。这就是它的全部内容。

您的代码知道它想要返回什么,即List<DemoClassTwo>,因此让调用者使用泛型重载指定其他类型并不是真的有用。

在某些情况下它可能有意义,但是你要求我们在它确实有意义的情况下做出一个例子,这没有任何意义。

给出一个合理的示例,您想要查询实体的上下文,您可以在基本权限上创建过滤器:

public abstract class BaseEntity
{
    public DateTime? DeletedOn { get; set; }
}

public class NotDeletedFilter<TEntity> 
    where TEntity : BaseEntity
{
    public IQueryable<TEntity> Filter(IQueryable<TEntity> entities)
    {
        return entities.Where(e => e.DeletedOn == null);
    }
}

现在,您可以将此过滤器应用于其实体继承自BaseEntity的任何可查询实体集合。

如果您使用的界面BaseEntity代替了IBaseEntity,那么您最好使用它。但有时您没有现有的界面,您无法修改与通用方法一起使用的类型。在这种情况下,您必须使用共享基类。

答案 1 :(得分:1)

这两种方法并不相同。我认为没有泛型的第二个只是一个例子,以显示通用的行为......

无论如何:

public List<T> Get<T>() where T : DemoClassTwo, new()

表示该方法期望T为类型(或子类型)DemoClassTwo,并且具有无参数的公共构造函数。

public List<DemoClassTwo> Get()

表示该方法将返回List<DemoClassTwo>

想象一下,你有一个班级DemoClassThree

public class DemoClassThree : DemoClassTwo
{
    public DemoClassThree() {
       Console.Write("New instance created !");
    }
}

然后,您可以像这样调用通用Get方法,并获得List<DemoClassThree>

var demoClassOneInstance = new DemoClassOne()
List<DemoClassThree> result =  demoClassOneInstance.Get<DemoClassThree>()

你将进入你的控制台:

  

创建了新实例!

但如果你试图这样做:

public class DemoClassFour
{
    public DemoClassFour() {
       Console.Write("New instance of DemoClassFour created !");
    }
}

List<DemoClassFour> result =  demoClassOneInstance.Get<DemoClassFour>()

...您将收到编译错误,因为DemoClassFour不会继承DemoClassTwo。

正如Icepickle所说,你只受到你想象力的限制......或者至少是真实的用例:)

答案 2 :(得分:0)

您可以将通用类型参数限制为仅包含某些Base ClassesInterfaces。如果我扩展你的例子并作出一些假设。 DemoClassTwo意味着你可能有DemoClassOne

现在,通过扩展程序,您还可以拥有DemoClassBaseIDemoClass

在这种情况下,您可能希望存储实现IDemoClass接口的任何类型。

对于通用可以使用的类型的限制,您可以确保您的项目的某个功能子集可能存在于该接口的多个不同实现中。

DemoClassBase类似,您可能不关心在扩展DemoClassBase的类上定义了哪些其他方法,但是使用Generic Type Parameters创建类实例的代码确实如此。因此,您可以将DemoBaseClass指定为元素必须扩展的内容。这可以确保代码知道它至少可以访问基类上定义的那组方法。

答案 3 :(得分:0)

我认为完全最好的解释是Lasse Vågsæther Karlsen所说的

public List<T> Get<T>() where T : DemoClassTwo

可以返回DemoClassTwo及其每个子类,但

public List<DemoClassTwo> Get()只能返回DemoClassTwo