在C#中是否存在具有参数约束的通用构造函数?

Wil*_*sem 156 c# generics parameters constructor generic-constraints

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

public class A {

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

}
Run Code Online (Sandbox Code Playgroud)

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

以下代码无法编译:

public class A {

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

}
Run Code Online (Sandbox Code Playgroud)

解决方法也有用吗?

Tim*_*son 139

正如您所发现的那样,您无法做到这一点.

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

public class A {

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

}
Run Code Online (Sandbox Code Playgroud)

  • 参数化构造函数约束是否因逻辑原因而不存在,或者它只是尚未添加到语言中的东西? (54认同)
  • @Matthew并非每个类都有无参数构造函数,如果你定义一个带参数的构造函数并且不重新定义默认构造函数,那么就有_no_默认构造函数. (44认同)
  • 同意......我们应该有`new(float,double)`,`new(string)`等等. (31认同)
  • @Matthew这是泛型类型约束的全部要点.您需要一个类,该类派生自某个类*并包含具有特定参数*的构造函数. (24认同)
  • @ bc3tech,从技术上讲,你的观点并非100%正确.如果基类没有默认构造函数,则必须提供一个调用其中一个基类构造函数的构造函数.您不必强制提供匹配的构造函数.这里有一个微妙的区别...... (13认同)
  • @Sahuagin我认为这是不可能的,因为当你从类继承时,不能保证子类具有定义的构造函数,因为构造函数不是继承的.但是每个类都有一个空参数构造函数. (12认同)
  • @Johnny5只能有一个短路 (6认同)
  • @Matthew,如果从基类继承,则必须创建匹配的构造函数(除非只存在默认构造函数).至少在C#中无论如何 (5认同)
  • @TimRobinson你会提供一个调用方法的样本吗? (2认同)

小智 46

使用反射创建一个通用对象,该类型仍然需要声明正确的构造函数,否则将抛出异常.您可以传入任何参数,只要它们与其中一个构造函数匹配即可.

使用这种方式,您不能在模板中的构造函数上设置约束.如果缺少构造函数,则需要在运行时处理异常,而不是在编译时获取错误.

// 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);
Run Code Online (Sandbox Code Playgroud)


Jar*_*Par 42

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

我用lambda方法解决这个问题.

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

用例

Method(x => new Foo(x));
Run Code Online (Sandbox Code Playgroud)


Dav*_*eau 16

这是一个解决方法,我个人觉得非常有效.如果您考虑泛型参数化构造函数约束是什么,它实际上是具有特定签名的类型和构造函数之间的映射.您可以使用字典创建自己的此类映射.将它们放在一个静态的"工厂"类中,你可以创建不同类型的对象,而不必担心每次都构建一个构造函数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) }
   };
Run Code Online (Sandbox Code Playgroud)

然后在你的泛型方法中,例如:

   public static T BuildBaseType<T>(...)
      where T : BaseType
   {
      ...
      T myObject = (T)mTypeConstructors[typeof(T)](value1, value2);
      ...
      return myObject;
   }
Run Code Online (Sandbox Code Playgroud)


Sea*_*lly 6

不可以.目前唯一可以指定的构造函数约束是for-arg构造函数.


Mik*_*erk 6

我认为这是对对象构造方式施加限制的最干净的解决方案。它没有完全检查编译时。当您同意使类的实际构造函数具有与 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)