Создать экземпляр универсального типа?
если BaseFruit имеет конструктор, который принимает int weight, могу ли я создать экземпляр фрукта в общем методе, как это?
public void AddFruit<T>()where T: BaseFruit{
BaseFruit fruit = new T(weight); /*new Apple(150);*/
fruit.Enlist(fruitManager);
}
пример добавлен за комментарии. Кажется, я могу сделать это только если я даю BaseFruit конструктор без параметров, а затем залить все через переменные-члены. В моем реальном коде (не о фруктах) это довольно непрактично.
-обновить-
Так что, похоже, он никак не может быть решен ограничениями затем. Из ответов есть три возможных решения:
- Шаблон Фабрики
- отражение
- активатор
Я склонен думать, что отражение является наименее чистым, но я не могу решить между двумя другими.
8 ответов:
дополнительно более простой пример:
return (T)Activator.CreateInstance(typeof(T), new object[] { weight });обратите внимание, что использование ограничения new() на T только для проверки компилятором открытого конструктора без параметров во время компиляции, фактический код, используемый для создания типа, является классом активатора.
вам нужно будет убедиться, что существует конкретный конструктор, и такое требование может быть запахом кода (или, скорее, что-то, чего вы должны просто попытаться избежать в текущей версии с.)#
вы не можете использовать параметризованный конструктор. Вы можете использовать конструктор без параметров, если у вас есть "
where T : new()" ограничение.это боль, но такова жизнь: (
Это одна из вещей, которые я хотел бы рассмотреть с "статические интерфейсы". Затем вы сможете ограничить T, чтобы включить статические методы, операторы и конструкторы, а затем вызвать их.
да; измените свое место:
where T:BaseFruit, new()однако, это работает только с параметров конструкторы. У вас должны быть какие-то другие средства настройки вашего свойства (установка самого свойства или что-то подобное).
Как указал Джон, это жизнь для ограничения непараметрического конструктора. Однако другим решением является использование заводского шаблона. Это легко сдерживается
interface IFruitFactory<T> where T : BaseFruit { T Create(int weight); } public void AddFruit<T>( IFruitFactory<T> factory ) where T: BaseFruit { BaseFruit fruit = factory.Create(weight); /*new Apple(150);*/ fruit.Enlist(fruitManager); }еще один вариант-использовать функциональный подход. Переходим в заводской метод.
public void AddFruit<T>(Func<int,T> factoryDel) where T : BaseFruit { BaseFruit fruit = factoryDel(weight); /* new Apple(150); */ fruit.Enlist(fruitManager); }
Вы можете сделать с помощью отражения:
public void AddFruit<T>()where T: BaseFruit { ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) }); if (constructor == null) { throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor"); } BaseFruit fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit; fruit.Enlist(fruitManager); }EDIT: добавлен конструктор == значение null проверить.
EDIT: более быстрый вариант с использованием кэша:
public void AddFruit<T>()where T: BaseFruit { var constructor = FruitCompany<T>.constructor; if (constructor == null) { throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor"); } var fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit; fruit.Enlist(fruitManager); } private static class FruitCompany<T> { public static readonly ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) }); }
недавно я столкнулся с очень похожей проблемой. Просто хотел поделиться своим решением со всеми вами. Я хотел, чтобы я создал экземпляр
Car<CarA>из объекта json, с помощью которого было перечисление:Dictionary<MyEnum, Type> mapper = new Dictionary<MyEnum, Type>(); mapper.Add(1, typeof(CarA)); mapper.Add(2, typeof(BarB)); public class Car<T> where T : class { public T Detail { get; set; } public Car(T data) { Detail = data; } } public class CarA { public int PropA { get; set; } public CarA(){} } public class CarB { public int PropB { get; set; } public CarB(){} } var jsonObj = {"Type":"1","PropA":"10"} MyEnum t = GetTypeOfCar(jsonObj); Type objectT = mapper[t] Type genericType = typeof(Car<>); Type carTypeWithGenerics = genericType.MakeGenericType(objectT); Activator.CreateInstance(carTypeWithGenerics , new Object[] { JsonConvert.DeserializeObject(jsonObj, objectT) });
это все еще возможно, с высокой производительностью, выполнив следующие действия:
// public List<R> GetAllItems<R>() where R : IBaseRO, new() { var list = new List<R>(); using ( var wl = new ReaderLock<T>( this ) ) { foreach ( var bo in this.items ) { T t = bo.Value.Data as T; R r = new R(); r.Initialize( t ); list.Add( r ); } } return list; }и
// ///<summary>Base class for read-only objects</summary> public partial interface IBaseRO { void Initialize( IDTO dto ); void Initialize( object value ); }соответствующие классы затем должны быть получены из этого интерфейса и инициализировать соответственно. Обратите внимание, что в моем случае, этот код является частью окружающего класса, который уже имеет
в качестве параметра универсального. R, в моем случае, также является классом только для чтения. ИМО, общедоступность функций Initialize () не оказывает негативного влияния на неизменность. Пользователь этот класс может поместить другой объект, но это не изменит базовую коллекцию.
Comments