Оператор как и универсальные классы - PullRequest
27 голосов
/ 28 марта 2009

Я пишу .NET On-the-Fly компилятор для сценариев CLR и хочу, чтобы метод выполнения сделал универсальный приемлемым:

object Execute()
{
  return type.InvokeMember(..);
}

T Execute<T>()
{
  return Execute() as T; /* doesn't work:
  The type parameter 'T' cannot be used with the 'as' operator because
  it does not have a class type constraint nor a 'class' constraint */

  // also neither typeof(T) not T.GetType(), so on are possible

  return (T) Execute(); // ok
}

Но я думаю, что оператор as будет очень полезен: если тип результата не T, метод вернет null вместо исключения! Можно ли это сделать?

Ответы [ 5 ]

56 голосов
/ 28 марта 2009

Вам нужно добавить

where T : class

к объявлению вашего метода, например

T Execute<T>()  where T : class
{

Кстати, в качестве предположения, что универсальная обертка действительно не добавляет особой ценности. Звонящий может написать:

MyClass c = whatever.Execute() as MyClass;

Или, если они хотят бросить при неудаче:

MyClass c = (MyClass)whatever.Execute();

Общий метод-оболочка выглядит следующим образом:

MyClass c = whatever.Execute<MyClass>();

Все три версии должны указывать одни и те же три сущности, только в разных порядках, поэтому ни одна из них не является более простой или более удобной, и все же общая версия скрывает то, что происходит, тогда как каждая из «сырых» версий проясняет это. будет ли бросок или null.

(Это может быть неуместно для вас, если ваш пример упрощен от вашего фактического кода).

11 голосов
/ 28 марта 2009

Вы не можете использовать оператор as с универсальным типом без ограничений. Поскольку оператор as использует null для представления того, что он не относится к типу, его нельзя использовать для типов значений. Если вы хотите использовать obj as T, T будет иметь в качестве ссылочного типа.

T Execute<T>() where T : class
{
  return Execute() as T;
}
3 голосов
/ 15 октября 2018

Этот небольшой фрагмент кода является безопасной заменой для ключевого слова :

return Execute() is T value ? value : default(T)

Он использует функцию сопоставления с образцом, представленную в C # 7. Используйте его, если вы не хотите ограничивать универсальный параметр ссылочным типом

1 голос
/ 28 марта 2009

Есть ли вероятность, что Execute () может вернуть тип значения? Если это так, то вам нужен метод Earwicker для типов классов и другой универсальный метод для типов значений. Может выглядеть так:

Nullable<T> ExecuteForValueType<T> where T : struct

Логика внутри этого метода сказала бы

object rawResult = Execute();

Затем вам нужно получить тип rawResult и посмотреть, можно ли его присвоить T:

Nullable<T> finalReturnValue = null;

Type theType = rawResult.GetType();
Type tType = typeof(T);

if(tType.IsAssignableFrom(theType))
{
    finalReturnValue = tType;     
}

return finalReturnValue;

Наконец, заставьте ваше исходное сообщение Execute выяснить, какой тип T имеет (тип класса или структуры), и вызовите соответствующую реализацию.

Примечание: это из грубой памяти. Я сделал это около года назад и, вероятно, не помню каждой детали. Тем не менее, я надеюсь, что направить вас в общем направлении поможет.

1 голос
/ 28 марта 2009

Похоже, вы просто добавляете метод-обертку для приведения к типу, который хочет пользователь, таким образом, добавляя только накладные расходы к выполнению. Для пользователя пишем

int result = Execute<int>();

мало чем отличается от

int result = (int)Execute();

Вы можете использовать модификатор out , чтобы записать результат в переменную в области видимости вызывающего и вернуть логический флаг, чтобы сказать, удалось ли это:

bool Execute<T>(out T result) where T : class
{
    result = Execute() as T;
    return result != null;
}
...