Добавление общих ограничений во время выполнения?
Я очень смущен этим, поэтому, если у кого-то есть идеи. У меня есть общий метод
public void Foo<TClass>(TClass item) where TClass : class
{ }
И я хочу вызвать этот метод из другого универсального метода, но этот общий метод не имеет ограничения типа "где TClass: class"
public void Bar<T>(T item)
{
this.Foo<T>(item);
}
Это не работает, я получаю сообщение об ошибке
"Тип" T "должен быть ссылочным типом, чтобы использовать его как параметр" TClass ""
Что я понимаю. Но мой вопрос заключается в следующем: есть ли что-нибудь, что я могу сделать с синтаксисом С#, чтобы "фильтровать" общий тип "T", чтобы передать его в "this.Bar", если это класс. Что-то вроде....
public void Bar<T>(T item)
{
if (typeof(T).IsClass)
this.Foo<T **as class**>();
}
Я понимаю, что могу использовать отражение, чтобы вызвать Foo, но это просто похоже на обман. Есть ли что-то, что я могу сделать с С# для передачи "T" с ограничением во время выполнения?
Кроме того - я не могу изменить ограничение на метод "Бар", поскольку он исходит из интерфейса, поэтому ограничение должно соответствовать ограничению на интерфейсе
Ответы
Ответ 1
Единственный способ вызвать Foo
без отражения - передать item
одному из типов/классов в своей иерархии (после правильной проверки IsClass
).
Очевидно, существует только один тип в своей иерархии, который вы знаете априори: Object
.
public void Bar<T>(T item)
{
if (typeof(T).IsClass)
this.Foo((object) item);
}
Изменить:
Кроме того, в одном из комментариев, которые вы сказали, вы добавили ограничение class
для создания экземпляра T
. Вам это не нужно, вам нужно new
ограничение.
Ответ 2
К сожалению, нет никакого способа сделать это, не меняя Bar
на наличие общего ограничения class
или используя отражение. Для компиляции С# должен знать во время компиляции, что T
действительно является значением class
. Невозможно использовать динамический тест, например typeof(T).IsClass
, чтобы удовлетворить это ограничение времени компиляции.
Вы упомянули в вопросе, что вы не можете изменить Bar
, но похоже, что вы готовы принять возможность динамического сбоя. Возможно, вместо этого измените Foo
, чтобы не иметь ограничения, но вместо этого вызывать исключение, если T
не является типом класса
Ответ 3
if (typeof(T).IsClass)
{
this.GetType()
.GetMethod("Foo", System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public)
.Invoke(this, new object[] { item });
}
Ответ 4
Я считаю, что невозможно скомпилировать его. Для выполнения вызова вам нужно будет использовать отражение.
На самом деле. Вы можете обмануть, если будете содержать его внутри класса:
public class Container<T>
{
public Container(T value)
{
Value = value;
}
public T Value { get; private set; }
}
public void Bar<T>(T item)
{
this.Foo<Container<T>>(new Container<T>(item));
}
но это добавляет один уровень, который вам нужен для сквозного вызова, и делает типы менее понятными.