Is there a generic constructor with parameter constraint in C#?

C#GenericsParametersConstructorGeneric Constraints

C# Problem Overview


In C# you can put a constraint on a generic method like:

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

Where you specify that T should have a constructor that requires no parameters. I'm wondering whether there is a way to add a constraint like "there exists a constructor with a float[,] parameter?"

The following code doesn't compile:

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

A workaround is also useful?

C# Solutions


Solution 1 - C#

As you've found, you can't do this.

As a workaround I normally supply a delegate that can create objects of type T:

public class A {

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

}

Solution 2 - C#

Using reflection to create a generic object, the type still needs the correct constructor declared or an exception will be thrown. You can pass in any argument as long as they match one of the constructors.

Used this way you cannot put a constraint on the constructor in the template. If the constructor is missing, an exception needs to be handled at run-time rather than getting an error at compile time.

// 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);

Solution 3 - C#

There is no such construct. You can only specify an empty constructor constraint.

I work around this problem with lambda methods.

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

Use Case

Method(x => new Foo(x));

Solution 4 - C#

Here is a workaround for this that I personally find quite effective. If you think of what a generic parameterized constructor constraint is, it's really a mapping between types and constructors with a particular signature. You can create your own such mapping by using a dictionary. Put these in a static "factory" class and you can create objects of varying type without having to worry about building a constructor lambda every time:

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) }
   };

then in your generic method, for example:

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

Solution 5 - C#

No. At the moment the only constructor constraint you can specify is for a no-arg constructor.

Solution 6 - C#

I think this is the most clean solution that kind of puts a constraint on the way an object is constructed. It is not entirely compile time checked. When you have the agreement to make the actual constructor of the classes have the same signature like the IConstructor interface, it is kind of like having a constraint on the constructor. The Constructor method is hidden when working normally with the object, because of the explicit interface implementation.

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;
        }
    }
}

Solution 7 - C#

How about creating your generic class with constraints, here I chose struct and class to have value and reference types.

That way your constructor has a constraint on the values.

> class MyGenericClass 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; } > }

Implementation:

> MyGenericClass intGenericClass = new MyGenericClass(10, "Hello world"); > int val = intGenericClass.genericMethod(200);

Solution 8 - C#

Here's the recommended workaround by c# maintainers if you'd like to keep the constructor parameter-ful, call the constructor indirectly:

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

Where TService is a generic with a parameter-full constructor that I'd like to keep.

If you'd like to read up on how this method works: https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance?view=net-5.0#system-activator-createinstance(system-type-system-object-)

Aaaaand discussion by maintainers of C#: https://github.com/dotnet/csharplang/discussions/769

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWillem Van OnsemView Question on Stackoverflow
Solution 1 - C#Tim RobinsonView Answer on Stackoverflow
Solution 2 - C#xpressView Answer on Stackoverflow
Solution 3 - C#JaredParView Answer on Stackoverflow
Solution 4 - C#Dave CousineauView Answer on Stackoverflow
Solution 5 - C#Sean ReillyView Answer on Stackoverflow
Solution 6 - C#Mike de KlerkView Answer on Stackoverflow
Solution 7 - C#Pierre-David SabourinView Answer on Stackoverflow
Solution 8 - C#pixelpaxView Answer on Stackoverflow