3

我试图弄清楚如何在 C# 中使用模板。我写了这个:

public static List<TValue> deepCopyList<TValue>(List<TValue> src)
{
    List<TValue> arr = new List<TValue>();

    for (int i = 0; i < src.Count; i++)
    {
        arr.Add(new TValue(src[i]));   //Error on this line
    }

    return arr;
}

但我收到一个错误:

错误 CS0304:无法创建变量类型“TValue”的实例,因为它没有 new() 约束

4

5 回答 5

5

#1:带有接口的新约束

添加一个约束来TValue告诉编译器它有一个无参数的构造函数。您可以通过将关键字添加newTValue. 这样你至少可以构建一个项目。

您不能使用泛型参数类型的参数。但是您可以使用另一个约束来定义一些属性:

public interface IMyValue<TValue>
{
    void CopyFrom(TValue original);
}

public static List<TValue> deepCopyList<TValue>(List<TValue> src)
    where TValue: IMyValue<TValue>, new() // <== Setting the constraints of TValue.
{
    List<TValue> arr = new List<TValue>();

    for (int i = 0; i < src.Count; i++)
    {
        TValue value = new TValue();
        value.CopyFrom(src[i]);
        arr.Add(value); // No error.
    }

    return arr;
}

#2:使用 ICloneable

还有第二种解决方案,它有点相同。使值负责关闭它自己,使用ICloneable

public static List<TValue> deepCopyList<TValue>(List<TValue> src)
    where TValue: ICloneable // <== Setting the constraints of TValue.
{
    List<TValue> arr = new List<TValue>();

    for (int i = 0; i < src.Count; i++)
    {
        TValue value = (TValue)src[i].Clone();
        arr.Add(value); // No error.
    }

    return arr;
}

#3:使用激活器

但既然你想创建一个深度克隆,还有另一种方法,使用Activator. 此方法不是类型安全的,并且当类型不支持该构造函数调用时会产生运行时异常:

public static List<TValue> deepCopyList<TValue>(List<TValue> src)
{
    List<TValue> arr = new List<TValue>();

    for (int i = 0; i < src.Count; i++)
    {
        TValue value = (TValue)Activator.CreateInstance(typeof(TValue), src[i]);
        arr.Add(value);  // Possible runtime rror.
    }

    return arr;
}

上述方法也可以通过使用反射替换并获得正确ConstructorInfo并使用它来创建新项目。做同样的事情Activator并具有同样的风险。

顺便说一句:在 C# 中,它被称为“通用”,而不是 C++ 中的“模板”。

于 2013-05-24T08:51:44.313 回答
2

这里的问题是您正在调用带有参数的构造函数,因此new约束是不够的。您可以做的是像这样动态调用构造函数(然后您必须确保您的 TValue 类具有匹配的构造函数):

public static List<TValue> DeepCopyList<TValue>(List<TValue> values)
{
    List<TValue> list = new List<TValue>();
    var ctor = typeof(TValue).GetConstructor(new[] {typeof(TValue)});
    foreach (TValue value in values)
    {
        list.Add((TValue)ctor.Invoke(new object[] {value}));
    }
    return list; 
}

或使用 linq:

public static List<TValue> DeepCopyList<TValue>(List<TValue> values)
{
    return (from value in values 
      let ctor = typeof(TValue).GetConstructor(new[] {typeof(TValue)}) 
      select (TValue)ctor.Invoke(new object[] {value})).ToList();
}

使用示例:

public class Test
{
    public Test(Test test)
    {
        // Do what you want...
    }
}

List<Test> tests = new List<Test>() { new Test(null) };
List<Test> results = DeepCopyList(tests);  


否则,此链接也可能对您有所帮助:Passing arguments to C# generic new() of templated type

于 2013-05-24T08:57:54.397 回答
1

如果要使用new带有泛型参数的运算符,则需要指定类型参数必须具有默认构造函数,如下所示

public static List<T> deepCopyList<T>(List<T> src)
    where T : new()
{
    // I can now call new, like so
    var value = new T();
}

但是,这本身并没有太大帮助因为您没有原始副本,您只有一个新对象 - 不幸的是,您不能指定类型必须支持特定的构造函数,所以您不能调用复制构造函数或类似的东西。

我可能会做的是这个

public static List<T> deepCopyList<T>(List<T> src)
    where T : ICloneable
{
    var value = src[0].Clone();
}

这意味着您只能将此方法与支持的类型一起使用ICloneable,但这可能与您将获得的一样好,而无需恢复使用反射和其他技巧。

于 2013-05-24T09:03:50.157 回答
0

您的方法签名应如下所示:

public static List<TValue> deepCopyList<TValue>(List<TValue> src) where TValue : new()

但是,您可能还需要将对象初始化更改为具有空构造函数,然后设置任何属性:

TValue val = new TValue();
val.MyField = src[i]; // This only works if you further constrain the type of TValue
arr.Add(val);

另请参阅有关新约束的官方文档

于 2013-05-24T08:53:46.953 回答
-1

编译器不知道泛型类型可以用 new 实例化,除非您使用 where 约束明确指定它。

于 2013-05-24T08:48:11.223 回答