Как работать вокруг ограничений в универсальных ограничениях типа в C#?

Хорошо я ищу некоторый вход, я вполне уверен, это в настоящее время не поддерживается в.NET 3.5, но здесь идет.

Я хочу потребовать, чтобы универсальный тип передал в мой класс, чтобы иметь конструктора как это:

new(IDictionary<string,object>)

таким образом, класс был бы похож на это

public MyClass<T>  where T : new(IDictionary<string,object>)
{
  T CreateObject(IDictionary<string,object> values)
  {
    return new T(values);
  }
}

Но компилятор не поддерживает это, он действительно не знает то, что я спрашиваю.

Некоторые из Вас могли бы спросить, почему Вы хотите сделать это? Хорошо я работаю над любимым проектом ORM, таким образом, я получаю значения от DB и затем создаю объект и загружаю значения.

Я думал, что это будет более чисто для разрешения объекта, просто создают себя со значениями, которые я даю ему. Насколько я могу сказать, что у меня есть две опции:

1) Используйте отражение (которого я стараюсь избегать) захватить PropertyInfo [] массив и затем использовать это для загрузки значений.

2) потребуйте, чтобы T поддерживал интерфейс как так:

открытый интерфейс ILoadValues {освобождает LoadValues (значения IDictionary);}

и затем сделайте это

public MyClass<T> where T:new(),ILoadValues
{
  T CreateObject(IDictionary<string,object> values)
  {
    T obj = new T();
    obj.LoadValues(values);
    return obj;
  }
}

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

namespace DataSource.Data
{
  public class User
  {
    protected internal User(IDictionary<string,object> values)
    {
      //Initialize
    }
  }
}

Настолько же долго как MyClass<T> был в том же блоке, конструктор будет доступен. Я лично думаю, что ограничение Типа, по-моему, должно спросить (У меня есть доступ к этому конструктору? Я делаю, большой!)

Так или иначе любой вход приветствуется.

6
задан John Saunders 20 March 2010 в 22:11
поделиться

4 ответа

Если вы можете создать общий базовый класс для всех объектов, которые вы собираетесь передать MyClass в качестве параметров типа, то вы можете сделать следующее:

internal interface ILoadValues
{
    void LoadValues<TKey, TValue>(IDictionary<TKey, TValue> values);
}

public class Base : ILoadValues
{
    void ILoadValues.LoadValues<TKey, TValue>(IDictionary<TKey, TValue> values)
    {
        // Load values.
    }
}

public class MyClass<T>
    where T : Base, new()
{
    public T CreateObject(IDictionary<string,object> values)
    {
        ILoadValues obj = new T();
        obj.LoadValues(values);
        return (T)obj;
    }
}

Если у вас не может быть общей базы class, чем я думаю, вы должны пойти с решением, предложенным itowlson.

2
ответ дан 9 December 2019 в 22:32
поделиться

Вы не можете этого сделать. Ограничения new (конструктор) предназначены только для конструкторов без параметров. У вас не может быть ограничения для конструктора с определенными параметрами.

Однако я столкнулся с той же проблемой и, наконец, остановился на выполнении части «инъекции» с помощью метода, который предоставляется в одном из интерфейсов, который указан как ограничение (как показано в вашем коде ).

(Надеюсь, здесь кто-то нашел более элегантный ответ на эту проблему!)

1
ответ дан 8 December 2019 в 20:15
поделиться

Мне законно интересно, как вы будете загружать значения класса без использования рефлексии, если только у вас нет жестко закодированных методов для этого. Я уверен, что есть другой ответ, но мне не стыдно сказать, что у меня нет опыта в этом. Что касается того, что я написал для автозагрузки данных, у меня есть два базовых класса данных, с которыми я работаю: один объект и список. В единственном объекте (BaseDataClass) у меня есть такой метод.

    public virtual void InitializeClass(DataRow dr)
    {
        Type type = this.GetType();
        PropertyInfo[] propInfos = type.GetProperties();

        for (int i = 0; i < dr.ItemArray.GetLength(0); i++)
        {
            if (dr[i].GetType() != typeof(DBNull))
            {
                string field = dr.Table.Columns[i].ColumnName;
                foreach (PropertyInfo propInfo in propInfos)
                {
                    if (field.ToLower() == propInfo.Name.ToLower())
                    {
                        // get data value, set property, break
                        object o = dr[i];
                        propInfo.SetValue(this, o, null);
                        break;
                    }
                }
            }
        }
    }

А затем в списке данных

public abstract class GenericDataList<T> : List<T> where T : BaseDataClass
{
    protected void InitializeList(string sql)
    {
        DataHandler dh = new DataHandler(); // my general database class
        DataTable dt = dh.RetrieveData(sql); 
        if (dt != null)
        {
            this.InitializeList(dt);
            dt.Dispose();
        }
        dt = null;
        dh = null;
    }

    protected void InitializeList(DataTable dt)
    {
        if (dt != null)
        {
            Type type = typeof(T);
            MethodInfo methodInfo = type.GetMethod("InitializeClass");

            foreach (DataRow dr in dt.Rows)
            {
                T t = Activator.CreateInstance<T>();
                if (methodInfo != null)
                {
                    object[] paramArray = new object[1];
                    paramArray[0] = dr;
                    methodInfo.Invoke(t, paramArray);
                }

                this.Add(t);
            }
        }
    }
}

Я открыт для критики, потому что никто еще не просматривал этот код. Я единственный программист на своем рабочем месте, поэтому у меня нет других людей, которые могли бы подкинуть идеи. К счастью, теперь я наткнулся на этот сайт!

Редактирование: Знаете что? Глядя на это сейчас, я не вижу причин, почему бы мне просто не переписать последний метод как

        protected void InitializeList(DataTable dt)
        {
            if (dt != null)
            {
                Type type = typeof(T);

                foreach (DataRow dr in dt.Rows)
                {
                    T t = Activator.CreateInstance<T>();
                    (t as BaseDataClass).InitializeClass(dr);

                    this.Add(t);
                }
            }
        }

Я предполагаю, что это работает, хотя я не проверял это. Нет необходимости использовать отражение в этой части.

1
ответ дан 9 December 2019 в 22:32
поделиться

Как сказал stakx, вы не можете сделать это с общим ограничением. В прошлом я использовал обходной путь: конструктор общего класса принимает фабричный метод, который он может использовать для создания T:

public class MyClass<T>
{
  public delegate T Factory(IDictionary<string, object> values);

  private readonly Factory _factory;

  public MyClass(Factory factory)
  {
    _factory = factory;
  }

  public T CreateObject(IDictionary<string, object> values)
  {
    return _factory(values);
  }
}

Используется следующим образом:

MyClass<Bob> instance = new MyClass<Bob>(dict => new Bob(dict));
Bob bob = instance.CreateObject(someDictionary);

Это дает вам безопасность типов во время компиляции, за счет немного более запутанной схемы построения и возможности того, что кто-то может передать вам делегат, который на самом деле не создает новый объект (что может быть или не быть серьезной проблемой в зависимости от того, насколько строгой вы хотите видеть семантику CreateObject).

8
ответ дан 9 December 2019 в 22:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: