Подскажите, пожалуйста, у меня есть два класса; (Star,Circle)
Есть ли возможность передать данный класс в метод с вызовом его конструктора.
Сейчас использую дженереки, но конструктор не работает
Пример:
public static T getObj
Сам вызов:
getObj
Ошибка: CS0417 'T": при создании экземпляра типа переменной не удается задать аргументы
как верно передать класс в метод заместо "new T"?
Ответ
То, в каком виде пытаетесь это сделать Вы, сделать не получится. Вы не можете наложить ограничение на T, чтобы у него обязательно был конструктор с указанными Вами типами параметров.
where T : new()
Данное ограничение new работает следующим образом:
Оно указывает, что аргумент любого переданного типа
должен иметь открытый конструктор без
параметров.
Как видите, нам это не подходит. Точнее, не совсем.Давайте рассмотрим несколько вариантов решения Вашей проблемы
#0. Наследование
Раз Вы пытаетесь объединить объекты, значит, они явно наделены общей логикой. А общую логику в ООП принято выносить в абстракции. Давайте так и поступим!
Я не знаю деталей реализации Вашей программы, но, судя по названиям, Вы работаете с некими фигурами. Запишем все это дело так:
// Абстрактный родительский класс
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
return obj;
}
Если же Вам нужно не просто установить значения свойств и в инициализатор спрятана какая-то хитрая логика, то метод можно переписать так:
// Добавим ограничение, что T может являться только потомком Shape
public static T GetShape
// Вручную сопоставим типы
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
// Используем паттерны сопоставления
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;
}
#1. Явное обращение к рефлексии
То, что Вы хотите, можно реализовать и с помощью нашей дорогой и любимой рефлексии (однако сразу предупреждаю, что это медленнее вышеописанного подхода. Не очень заметно на малых выборках, но все же именно поэтому рекомендуется использовать варианты выше)
От слов к делу:
public static T GetShape
// Получаем открытый конструктор, в который надо передать два объекта типа 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
Данный метод делает ровно то, что и предыдущий (описанный для Вашего лучшего понимания подноготной), только встроенными возможностями
С помощью рефлексии можно реализовать метод безо всякого наследования и сопоставления типов, так что метод динамически расширяем (то есть если Вы добавите еще 10 классов, реализующих указанный конструктор, Вам не придется менять данный метод для их создания, в отличие от описанных выше с помощью наследования)
UPD:
Спасибо @PashaPash за поправку!
Итак, согласно документации
T obj = new T();
также использует рефлексию для создания объекта.То есть данное выражение эквивалентно следующему:
T obj = Activator.CreateInstance
Надеюсь, мой ответ помог Вам разобраться в вопросе и решил Вашу проблему! Удачи в Ваших начинаниях!
Комментариев нет:
Отправить комментарий