Хорошо я ищу некоторый вход, я вполне уверен, это в настоящее время не поддерживается в.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>
был в том же блоке, конструктор будет доступен. Я лично думаю, что ограничение Типа, по-моему, должно спросить (У меня есть доступ к этому конструктору? Я делаю, большой!)
Так или иначе любой вход приветствуется.
Если вы можете создать общий базовый класс для всех объектов, которые вы собираетесь передать 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.
Вы не можете этого сделать. Ограничения new
(конструктор) предназначены только для конструкторов без параметров. У вас не может быть ограничения для конструктора с определенными параметрами.
Однако я столкнулся с той же проблемой и, наконец, остановился на выполнении части «инъекции» с помощью метода, который предоставляется в одном из интерфейсов, который указан как ограничение (как показано в вашем коде ).
(Надеюсь, здесь кто-то нашел более элегантный ответ на эту проблему!)
Мне законно интересно, как вы будете загружать значения класса без использования рефлексии, если только у вас нет жестко закодированных методов для этого. Я уверен, что есть другой ответ, но мне не стыдно сказать, что у меня нет опыта в этом. Что касается того, что я написал для автозагрузки данных, у меня есть два базовых класса данных, с которыми я работаю: один объект и список. В единственном объекте (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);
}
}
}
Я предполагаю, что это работает, хотя я не проверял это. Нет необходимости использовать отражение в этой части.
Как сказал 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).