Есть ли общий конструктор с ограничением параметров в C #? - PullRequest
152 голосов
/ 05 декабря 2009

В 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...
    }

}

Обходной путь также полезен?

Ответы [ 7 ]

133 голосов
/ 05 декабря 2009

Как вы обнаружили, вы не можете сделать это.

В качестве обходного пути я обычно предоставляю делегата, который может создавать объекты типа T:

public class A {

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

}
41 голосов
/ 05 декабря 2009

Нет такой конструкции. Вы можете указать только пустое ограничение конструктора.

Я работаю над этой проблемой с помощью лямбда-методов.

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

Вариант использования

Method(x => new Foo(x));
40 голосов
/ 26 марта 2012

Используя рефлексию для создания универсального объекта, типу все еще нужен правильный объявленный конструктор, иначе будет выдано исключение. Вы можете передать любой аргумент, если он соответствует одному из конструкторов.

При использовании этого способа вы не можете наложить ограничение на конструктор в шаблоне. Если конструктор отсутствует, исключение необходимо обрабатывать во время выполнения, а не получать ошибку во время компиляции.

// 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);
16 голосов
/ 08 февраля 2012

Вот обходной путь для этого, который я лично считаю довольно эффективным. Если вы подумаете о том, что такое общее ограничение параметризованного конструктора, это действительно отображение между типами и конструкторами с определенной сигнатурой. Вы можете создать свое собственное такое отображение, используя словарь. Поместите их в статический «фабричный» класс, и вы сможете создавать объекты различного типа, не беспокоясь о построении лямбда-конструктора каждый раз:

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;
   }
6 голосов
/ 05 декабря 2009

Нет. На данный момент единственное ограничение конструктора, которое вы можете указать, - это конструктор без аргументов.

3 голосов
/ 05 апреля 2018

Я думаю, что это самое чистое решение, которое как бы накладывает ограничение на способ создания объекта. Это не полностью проверено время компиляции. Когда у вас есть соглашение, чтобы фактический конструктор классов имел ту же сигнатуру, что и интерфейс 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;
        }
    }
}
1 голос
/ 03 апреля 2019

Как насчет создания вашего универсального класса с ограничениями, здесь я выбрал struct и class, чтобы иметь значения и ссылочные типы.

Таким образом, ваш конструктор имеет ограничение на значения.

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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...