wake-up-neo.com

Gibt es einen generischen Konstruktor mit Parameterbeschränkung in C #?

In C # können Sie eine Einschränkung für eine generische Methode festlegen, z.

public class A {

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

}

Wenn Sie angeben, dass T einen Konstruktor haben soll, für den keine Parameter erforderlich sind. Ich frage mich, ob es eine Möglichkeit gibt, eine Einschränkung wie " hinzuzufügen. Es gibt einen Konstruktor mit einem float[,] Parameter? "

Der folgende Code wird nicht kompiliert:

public class A {

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

}

Eine Problemumgehung ist auch nützlich?

155

Wie Sie festgestellt haben, können Sie dies nicht tun.

Als Workaround stelle ich normalerweise einen Delegaten zur Verfügung, der Objekte vom Typ T erstellen kann:

public class A {

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

}
138
Tim Robinson

Es gibt kein solches Konstrukt. Sie können nur eine leere Konstruktoreinschränkung angeben.

Ich arbeite mit Lambda-Methoden um dieses Problem herum.

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

Anwendungsfall

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

Bei Verwendung von Reflection zum Erstellen eines generischen Objekts muss für den Typ immer noch der richtige Konstruktor deklariert werden, oder es wird eine Ausnahme ausgelöst. Sie können jedes Argument übergeben, solange es mit einem der Konstruktoren übereinstimmt.

Auf diese Weise können Sie den Konstruktor in der Vorlage nicht einschränken. Wenn der Konstruktor fehlt, muss eine Ausnahme zur Laufzeit behandelt werden, anstatt beim Kompilieren einen Fehler zu erhalten.

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

Hier ist ein Workaround dafür, den ich persönlich sehr effektiv finde. Wenn Sie sich vorstellen, was eine generische parametrisierte Konstruktor-Einschränkung ist, dann ist dies eine Zuordnung zwischen Typen und Konstruktoren mit einer bestimmten Signatur. Sie können eine solche Zuordnung mithilfe eines Wörterbuchs selbst erstellen. Fügen Sie diese in eine statische "Factory" -Klasse ein, und Sie können Objekte unterschiedlichen Typs erstellen, ohne sich jedes Mal Gedanken über die Erstellung eines Konstruktor-Lambda machen zu müssen:

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

dann in Ihrer generischen Methode, zum Beispiel:

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

Nein. Derzeit können Sie nur eine Konstruktor-Einschränkung für einen Konstruktor ohne Argument angeben.

6
Sean Reilly

Ich denke, dies ist die sauberste Lösung, die die Art und Weise, wie ein Objekt konstruiert wird, einschränkt. Die Kompilierungszeit ist nicht vollständig überprüft. Wenn Sie die Vereinbarung haben, dass der tatsächliche Konstruktor der Klassen die gleiche Signatur wie die IConstructor-Schnittstelle hat, hat dies eine Art Einschränkung für den Konstruktor. Die Methode Constructor ist aufgrund der expliziten Schnittstellenimplementierung ausgeblendet, wenn Sie normal mit dem Objekt arbeiten.

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;
        }
    }
}
3
Mike de Klerk

Wie wäre es, eine generische Klasse mit Einschränkungen zu erstellen? Hier habe ich Struktur und Klasse ausgewählt, um Wert- und Referenztypen zu haben.

Auf diese Weise hat Ihr Konstruktor eine Einschränkung für die Werte.

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

Implementierung:

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