为什么可以';t我通过列表<;T>;到泛型构造函数参数,尽管满足了所有约束

本文关键字:泛型 参数 构造函数 约束 满足 为什么 lt 列表 gt | 更新日期: 2023-09-27 18:28:04

我有以下代码:

public class SampleClass<T, TCollection> where TCollection : class, IList<T>, IReadOnlyList<T>
{
    private readonly TCollection _collection;
    public SampleClass() : this(new List<T>()) { }
    public SampleClass(TCollection collection)
    {
        _collection = collection;
    }
}

遗憾的是,包含默认构造函数public SampleClass() : this(new List<T>()) { }的行没有编译。错误消息显示:参数1:无法从"System.Collections.Generic.List"转换为"TCollection"。

为什么会发生这种情况?List<T>实际上满足了我对TCollection泛型的所有限制:它是一个实现IList<T>IReadOnlyList<T>的类。

非常感谢你的帮助。

编辑:已更新代码,因为其中包含错误

为什么可以';t我通过列表<;T>;到泛型构造函数参数,尽管满足了所有约束

为什么会发生这种情况?

问题是我可以写这样的代码:

class X : IList<int>, IReadOnlyList<int>
{
    ...
}

然后试着这样使用它:

var instance = new SampleClass<int, X>();

但没有从List<int>X的转换。

这种情况的可能性是为什么你会得到编译器错误消息:

参数1:无法从"System.Collections.Generic.List"转换为"TCollection"。

要获得您正在寻找的"默认List<T>"行为,您可以去掉该默认构造函数,并将其替换为以下内容:

static class SampleClass
{
    public static SampleClass<T, List<T>> Create<T>()
    {
        return new SampleClass<T, List<T>>(new List<T>());
    }
}

这是一种类似于Tuple静态类的模式,具有静态Tuple.Create泛型方法和非静态Tuple<T1>Tuple<T1, T2>等类。

将泛型约束更改为TCollection并添加new()约束。然后可以使用new TCollection() 初始化类

public class SampleClass<T, TCollection> where TCollection : class, IList<T>, IReadOnlyList<T>, new()
{
    private readonly TCollection _collection;
    public SampleClass() : this(new TCollection()) { }
    public SampleClass(TCollection collection)
    {
        _collection = collection;
    }
}

您的方法的问题是,您假设可以将List<T>分配给TCollection类型的引用,而实际上不知道TCollection是什么。这里可以使用两种方法。前者更接近于您目前拥有的内容,而后者允许您编写一个更容易在各种场景中使用的类。

  1. TCollection使用new约束。虽然您仍然不知道TCollection是什么类型,但编译器要求该类型具有默认构造函数,允许您编写以下内容:

    public class SampleClass<T, TCollection>
        where TCollection : IList<T>, IReadOnlyList<T>, new()
    {
        public SampleClass() : this(new TCollection()) { }
        public SampleClass(TCollection collection) { ... }
    }
    
  2. 需要在TCollection的实例中调用代码传递,而不是自己创建它。在这种情况下,您只需删除默认构造函数。

我相信Sam Harwell已经给出了部分答案,你真的希望TCollection是包含T元素的东西,所以你应该有:

public class SampleClass<T, TCollection>
  where TCollection : class, IList<T>, IReadOnlyList<T>

然而,这还不足以处理这样一个事实,即您想要一个无参数构造函数,将new List<T>()传递给接受TCollection参数的构造函数。某些东西可能是TCollection,而不是List<T>或其基础,因此没有足够的约束来保证这一点。

有三种可能的方法。第一,完全取消无参数构造函数:

public class SampleClass<T, TCollection> where TCollection : class, IList<T>, IReadOnlyList<T>, new()
{
  private readonly TCollection _collection;
  public SampleClass(TCollection collection)
  {
    _collection = collection;
  }
}

第二,将传入的集合存储为List<T>,并坚持TCollection是它或从它派生(不太可能有用):

public class SampleClass<T, TCollection> where TCollection : List<T>
{
  private readonly List<T> _collection;
  public SampleClass() : this(new List<T>()) { }
  public SampleClass(TCollection collection)
  {
    _collection = collection;
  }
}

第三,坚持TCollection是一个具有无参数构造函数的类,并使用它:

public class SampleClass<T, TCollection> where TCollection : class, IList<T>, IReadOnlyList<T>, new()
{
  private readonly TCollection _collection;
  public SampleClass() : this(new TCollection()) { }
  public SampleClass(TCollection collection)
  {
    _collection = collection;
  }
}

TCollection是您定义的类吗?

List<T>实现:

public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>,
       IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable

TCollection是实现IListICollection,还是继承List<T>Collection<T>的基类?TCollection必须实现或继承其中一个才能消除编译错误。

您还需要将类TCollection更改为TCollection<T>,以便接受一般定义的new List<T>。泛型需要传递到某个地方的集合。

当然,除非你把约束放在定义什么是TCollection上。你可能需要注意的是,你把约束放到了通用<T>上,而不是TCollection上。