Подскажите, пожалуйста, у меня есть два класса; (Star,Circle) Есть ли возможность передать данный класс в метод с вызовом его конструктора.
Сейчас использую дженереки, но конструктор не работает
Пример:
public static T getObj<T>( int size, int i) where T : new()
{
return _objs[i] = new T(new Point(rnd.Next(0, 800), i * 20), new Point(5 - i, 15 - i), new Size(size, size));
}
Сам вызов:
getObj<Star>(3, i)
Ошибка: CS0417 'T": при создании экземпляра типа переменной не удается задать аргументы
как верно передать класс в метод заместо "new T"?
То, в каком виде пытаетесь это сделать Вы, сделать не получится.
Вы не можете наложить ограничение на T
, чтобы у него обязательно был конструктор с указанными Вами типами параметров.
where T : new()
Данное ограничение new работает следующим образом:
Оно указывает, что аргумент любого переданного типа должен иметь открытый конструктор без параметров.
Как видите, нам это не подходит. Точнее, не совсем.
Давайте рассмотрим несколько вариантов решения Вашей проблемы
Раз Вы пытаетесь объединить объекты, значит, они явно наделены общей логикой. А общую логику в ООП принято выносить в абстракции. Давайте так и поступим!
Я не знаю деталей реализации Вашей программы, но, судя по названиям, Вы работаете с некими фигурами. Запишем все это дело так:
// Абстрактный родительский класс
public abstract class Shape
{
// Свойства, которые будут у всех его потомков
public Point A { get; set; }
public Point B { get; set; }
public Size C { get; set; }
// Публичные инициализаторы
public Shape() { }
// Тот инициализатор, что Вы пытаетесь использовать
public Shape(Point A, Point B, Size C)
{
this.A = A;
this.B = B;
this.C = C;
}
}
public class Circle : Shape
{
// Реализовываем нужные инициализаторы
public Circle() { }
public Circle(Point A, Point B, Size C) : base(A, B, C)
{
// SOMETHING
}
}
public class Star : Shape
{
// Реализовываем нужные инициализаторы
public Star() { }
public Star(Point A, Point B, Size C) : base(A, B, C)
{
// SOMETHING
}
}
Теперь же давайте изменим Ваш метод:
// Добавим ограничение, что T может являться только потомком Shape
public static T GetShape<T>(Point A, Point B, Size C) where T : Shape, new()
{
// Используем инициализатор по умолчанию, установив все свойства вручную
T obj = new T
{
A = A,
B = B,
C = C
};
return obj;
}
Если же Вам нужно не просто установить значения свойств и в инициализатор спрятана какая-то хитрая логика, то метод можно переписать так:
// Добавим ограничение, что T может являться только потомком Shape
public static T GetShape<T>(Point A, Point B, Size C) where T : Shape
{
// Получим тип T
Type type = typeof(T);
// Вручную сопоставим типы
if (type == typeof(Star))
// Преобразуем Star к базовому классу, а после - к T,
// который, как указано в ограничении, является Shape
return (T)(Shape)new Star(A, B, C);
else
if (type == typeof(Circle))
// Преобразуем Circle к базовому классу, а после - к T,
// который, как указано в ограничении, является Shape
return (T)(Shape)new Circle(A, B, C);
// Если тип не опознан - выбросим ошибку
throw new NotSupportedException();
}
Или же можно использовать сопоставление шаблонов из C# 7.0+ (данный вариант представлен больше для демонстрации указанных возможностей языка, чем для использования в реальных проектах):
// Добавим ограничение, что T может являться только потомком Shape
public static T GetShape<T>(Point A, Point B, Size C) where T : Shape, new()
{
// Инициализатор по умолчанию
// Этот метод подразумевает создание объекта дважды,
// что не очень логически верно. Не делайте так)
T obj = new T();
// Используем паттерны сопоставления
switch (obj)
{
case Star star:
star = new Star(A, B, C);
obj = (T)(Shape)star;
break;
case Circle circle:
circle = new Circle(A, B, C);
obj = (T)(Shape)circle;
break;
default:
// Тип не распознан
throw new NotSupportedException();
}
return obj;
}
То, что Вы хотите, можно реализовать и с помощью нашей дорогой и любимой рефлексии (однако сразу предупреждаю, что это медленнее вышеописанного подхода. Не очень заметно на малых выборках, но все же именно поэтому рекомендуется использовать варианты выше)
От слов к делу:
public static T GetShape<T>(Point A, Point B, Size C)
{
// Получим тип T
Type type = typeof(T);
// Получаем открытый конструктор, в который надо передать два объекта типа Point и один типа Size
ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(Point), typeof(Point), typeof(Size) });
if (constructor != null)
{
try
{
// Вызовем конструктор с указанными параметрами
object result = constructor.Invoke(new object[] { A, B, C });
// Если все прошло удачно, то приведем результат к указанному типу и вернем
if (result != null)
return (T)result;
}
catch (Exception ex)
{
// Ошибочка вышла, обработайте ее!
throw new NotSupportedException();
}
}
// У нас ничего не вышло, значит, тип не соответсвует заданным условиям
throw new NotSupportedException();
}
Метод можно порядком ужать. Я все расписываю, чтобы Вам было удобнее и понятнее читать)
Как ужать? Таким образом, используя Activator.CreateInstance:
public static T GetShape<T>(Point A, Point B, Size C)
{
try
{
return (T)Activator.CreateInstance(typeof(T), A, B, C);
}
catch (Exception ex)
{
// Обработайте ошибку. Ее появление означает,
// что у заданного типа нет нужного конструктора
throw new NotSupportedException();
}
}
Данный метод делает ровно то, что и предыдущий (описанный для Вашего лучшего понимания подноготной), только встроенными возможностями
С помощью рефлексии можно реализовать метод безо всякого наследования и сопоставления типов, так что метод динамически расширяем (то есть если Вы добавите еще 10 классов, реализующих указанный конструктор, Вам не придется менять данный метод для их создания, в отличие от описанных выше с помощью наследования)
Спасибо @PashaPash за поправку!
Итак, согласно документации
T obj = new T();
также использует рефлексию для создания объекта.
То есть данное выражение эквивалентно следующему:
T obj = Activator.CreateInstance<T>();
Надеюсь, мой ответ помог Вам разобраться в вопросе и решил Вашу проблему!
Удачи в Ваших начинаниях!
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
У меня возник вопрос, как же можно сделать пользовательскую настройку для приложенияУ меня существует несколько форм
ЗдравтсвуйтеЗадача: Построить мультиселект с опцией добавления к нему значений юзером
Необходимо на домене siteru открыть поддомен 111