为什么我不能将List <t>传递给通用构造函数参数,尽管满足所有约束条件?</t>

时间:2014-10-28 14:49:42

标签: c# generics

我有以下代码:

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:无法转换为&#39; System.Collections.Generic.List&#39;到&#39; TCollection&#39;

为什么会这样? List<T>实际上符合我对TCollection泛型的所有限制:它是一个实现IList<T>IReadOnlyList<T>的类。

非常感谢你的帮助。

编辑:更新了代码,因为它包含错误。

5 个答案:

答案 0 :(得分:5)

  

为什么会这样?

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

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

然后尝试使用它:

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

但是没有从List<int>转换为X

此方案的可能性是您收到编译器错误消息的原因:

  

参数1:无法转换为System.Collections.Generic.List&#39;到&#39; TCollection&#39;。

获取List<T>&#34;的默认值您正在寻找的行为,您可以摆脱该默认构造函数并将其替换为以下内容:

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>等类的模式。

答案 1 :(得分:1)

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

  1. new使用TCollection约束。虽然你还不知道TCollection是什么类型,但编译器要求类型有一个默认构造函数,允许你写这个:

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

答案 2 :(得分:1)

将通用约束更改为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;
    }
}

答案 3 :(得分:1)

我相信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;
  }
}

答案 4 :(得分:0)

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设置了约束。