186

在 C# 中,您可以对通用方法施加约束,例如:

public class A {

    public static void Method<T> (T a) where T : new() {
        //...do something...
    }

}

您指定的地方T应该有一个不需要参数的构造函数。我想知道是否有办法添加一个约束,比如“存在一个带float[,]参数的构造函数?

以下代码无法编译:

public class A {

    public static void Method<T> (T a) where T : new(float[,] u) {
        //...do something...
    }

}

解决方法也有用吗?

4

8 回答 8

160

正如你所发现的,你不能这样做。

作为一种解决方法,我通常提供一个可以创建类型对象的委托T

public class A {

    public static void Method<T> (T a, Func<float[,], T> creator) {
        //...do something...
    }

}
于 2009-12-05T17:41:00.153 回答
55

使用反射创建泛型对象,该类型仍需要声明正确的构造函数,否则将引发异常。您可以传入任何参数,只要它们与构造函数之一匹配即可。

使用这种方式,您不能对模板中的构造函数施加约束。如果缺少构造函数,则需要在运行时处理异常,而不是在编译时出错。

// public static object CreateInstance(Type type, params object[] args);

// Example 1
T t = (T)Activator.CreateInstance(typeof(T));
// Example 2
T t = (T)Activator.CreateInstance(typeof(T), arg0, arg1, arg2, ...);
// Example 3
T t = (T)Activator.CreateInstance(typeof(T), (string)arg0, (int)arg1, (bool)arg2);
于 2012-03-26T08:13:13.080 回答
47

没有这样的结构。您只能指定一个空的构造函数约束。

我用 lambda 方法解决了这个问题。

public static void Method<T>(Func<int,T> del) {
  var t = del(42);
}

用例

Method(x => new Foo(x));
于 2009-12-05T17:40:09.203 回答
18

这是我个人认为非常有效的解决方法。如果您想到泛型参数化构造函数约束是什么,它实际上是类型和具有特定签名的构造函数之间的映射。您可以使用字典创建自己的此类映射。将它们放在静态“工厂”类中,您可以创建不同类型的对象,而不必担心每次都构建构造函数 lambda:

public static class BaseTypeFactory
{
   private delegate BaseType BaseTypeConstructor(int pParam1, int pParam2);

   private static readonly Dictionary<Type, BaseTypeConstructor>
   mTypeConstructors = new Dictionary<Type, BaseTypeConstructor>
   {
      { typeof(Object1), (pParam1, pParam2) => new Object1(pParam1, pParam2) },
      { typeof(Object2), (pParam1, pParam2) => new Object2(pParam1, pParam2) },
      { typeof(Object3), (pParam1, pParam2) => new Object3(pParam1, pParam2) }
   };

然后在您的通用方法中,例如:

   public static T BuildBaseType<T>(...)
      where T : BaseType
   {
      ...
      T myObject = (T)mTypeConstructors[typeof(T)](value1, value2);
      ...
      return myObject;
   }
于 2012-02-08T07:48:03.177 回答
8

不。目前,您可以指定的唯一构造函数约束是无参数构造函数。

于 2009-12-05T17:39:22.200 回答
7

我认为这是最干净的解决方案,它限制了对象的构造方式。它不是完全编译时检查的。当您同意使类的实际构造函数具有与 IConstructor 接口相同的签名时,这有点像对构造函数有约束。由于显式接口实现,该Constructor方法在与对象正常工作时被隐藏。

using System.Runtime.Serialization;

namespace ConsoleApp4
{
    class Program
    {
        static void Main(string[] args)
        {
            var employeeWorker = new GenericWorker<Employee>();
            employeeWorker.DoWork();
        }
    }

    public class GenericWorker<T> where T:IConstructor
    {
        public void DoWork()
        {
            T employee = (T)FormatterServices.GetUninitializedObject(typeof(T));
            employee.Constructor("John Doe", 105);
        }
    }

    public interface IConstructor
    {
        void Constructor(string name, int age);
    }

    public class Employee : IConstructor
    {
        public string Name { get; private set; }
        public int Age { get; private set; }

        public Employee(string name, int age)
        {
            ((IConstructor)this).Constructor(name, age);
        }

        void IConstructor.Constructor(string name, int age)
        {
            Name = name;
            Age = age;
        }
    }
}
于 2018-04-05T14:19:46.413 回答
3

如何创建带有约束的泛型类,这里我选择了结构和类来具有值和引用类型。

这样,您的构造函数对值有约束。

class MyGenericClass<T, X> where T :struct where X: class 
{
    private T genericMemberVariableT;
    private X genericMemberVariableX;
    public MyGenericClass(T valueT, X valueX)
    {
        genericMemberVariableT = valueT;
        genericMemberVariableX = valueX;
    }

    public T genericMethod(T genericParameter)
    {
        Console.WriteLine("Parameter type: {0}, value: {1}", typeof(T).ToString(), genericParameter);
        Console.WriteLine("Return type: {0}, value: {1}", typeof(T).ToString(), genericMemberVariableT);
        Console.WriteLine("Return type: {0}, value: {1}", typeof(X).ToString(), genericMemberVariableX);
        return genericMemberVariableT;
    }

    public T genericProperty { get; set; }
}

执行:

        MyGenericClass<int, string> intGenericClass = new MyGenericClass<int, string>(10, "Hello world");
        int val = intGenericClass.genericMethod(200);
于 2019-04-03T16:54:25.650 回答
2

这是 c# 维护者推荐的解决方法,如果您想保持构造函数参数化,请间接调用构造函数:

            i = (TService)Activator.CreateInstance(typeof(TService), new object[] {arg});

其中 TService 是具有我想保留的参数完整构造函数的泛型。

如果您想了解此方法的工作原理: https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance?view=net-5.0#system-activator-createinstance(系统类型系统对象)

Aaaa 和 C# 维护者的讨论: https ://github.com/dotnet/csharplang/discussions/769

于 2021-07-09T23:20:05.693 回答