Как передать тип методу - аргумент типа vs generics

У меня есть метод объекта, который похож на factory. Вы даете ему тип, он создает экземпляр и делает несколько других вещей. Элегантный способ сделать это (на мой взгляд) выглядит следующим образом:

public T MagicMethod<T>() where T: SomeBaseClass
{
    // Magic goes here
}

Но это расстраивает FxCop, который говорит, что это плохой стиль. Я получаю предупреждение о том, что "CA1004: общие методы должны предоставлять параметр типа". Что-то не в том, чтобы использовать выводы и прочее. Итак, единственный способ, которым я могу думать, это что-то вроде этого:

public SomeBaseClass MagicMethod(Type T)
{
    // Same magic goes here
}

Я считаю, что это уступает первому методу для многих учетных записей, но правило стиля... В статье MSDN о предупреждении даже говорится, что нет причин для его подавления.

Правильно ли я делаю это, подавляя это предупреждение?

Ответ 1

Предупреждения FXCop - это только предупреждения. Подобно неявным предупреждениям о броске, они служат для того, чтобы вы знали, что что-то, что вы делаете, может иметь поведение, которое вы не ожидаете, или, возможно, не так, как вы планировали.

Неявное предупреждение о броске рассматривается, просматривая код, определяя, действительно ли вы намеревались это сделать, и если да, добавив явное выражение.

То же самое с FXCop. Посмотрите на предупреждение, посмотрите на свой код и определите, действительно ли предупреждение. Если это так, исправьте. Если нет, подавите его. Подавление является эквивалентом явного приведения - "Да, FXCop, я уверен, что хочу это сделать".

Если бы это действительно было ошибкой, вероятно, это была ошибка компилятора.

Ответ 2

Я считаю, что вы неправильно понимаете, что говорит вам FxCop, возможно, потому, что его формулировка менее идеальна. Это означает, что общий метод должен предоставлять параметр этого типа, а не то, что общий метод должен иметь неосновную перегрузку, которая предоставляет экземпляр Type для выполнения. Например,

public void DoSomething<T>(T myParam);

myParam - это тип параметра, на который он ссылается. Причина, по которой это требуется, - это, как вы полагаете, для вывода. Это позволяет вам делать что-то вроде...

string foo = "bar";

DoSomething(foo);

вместо того, чтобы писать

DoSomething<string>(foo);

В вашем случае это прекрасно, чтобы подавить предупреждение, так как вы хотите, чтобы пользователь явно указывал тип. Однако я бы предположил, что (при условии, что ваши конструкторы без параметров) вы меняете свой where на where T : SomeBaseClass, new(). Это означает, что он заставит компилятор потребовать, чтобы какой бы тип не был передан без конструктора без параметров. Это также означает, что вы можете сделать new T() в своем коде.

Ответ 3

У меня не было бы проблемы с подавлением этого предупреждения. Для начала эквивалент в собственном коде MS Activator.CreateInstance<T>()

public static T CreateInstance<T>()

Это означает, что в правиле анализа следует учитывать, распространяется ли возвращаемый тип метода на общий параметр...

Это упоминалось во многих местах:

И были, например, предыдущие ошибки в правиле:

public static void GenericMethod<T>(List<T> arg);

ранее вызывал бы его (исправлено в 2005 SP1).

Я предлагаю подать ошибку подключения для вашего конкретного примера

Ответ 4

FxCop запустит это предупреждение, даже если вы используете параметр общего типа в одном или нескольких аргументах, если он не "разделен":

public void LinkedList<T> Slice<T>(LinkedList<T> collection, Predicate<T> match)
{
    ...
}

По крайней мере, правило "CA1004" запускается "по ошибке" здесь на днях по методу с этой сигнатурой.

Для того, чтобы быть умнее команды FxCop, я не уверен, что правила могут правильно определять код во всех случаях, что уровень доверия для:)

Ответ 5

Второй подход даже не эквивалентен первому. Во втором вам буквально задан тип, но вы не можете создать экземпляр объекта такого типа (если вы не используете Reflection --- eeek!), И вам нужно явно объявить тип возврата (который побеждает цель дженериков в начните с).

Смотрите эту заметку о ее подавлении. Похоже, что это нормально, чтобы подавить.

EDIT: Теперь вот еще одна идея. Что, если вы изменили его на параметр "out" и не вернули его через возвращаемую переменную? Изменит ли это предупреждение?

public void MagicMethod<T>( out T retVar ) where T: SomeBaseClass
{
    // Magic goes here
}

Ответ 6

Лично я буду беспокоиться о большинстве предупреждений в Fxcop.

Вы, кажется, знаете, что вы делаете, почему некоторые автоматизированные части программного обеспечения лучше знают?

Ну, это не возможно, это догадка.

Ответ 7

Прежде всего, это предупреждение, чтобы убедиться, что звонящие делают все сознательно. Вы можете вызвать ваш метод без передачи какого-либо параметра типа, потому что компилятор знает тип объекта перед рукой. FxCop говорит вам, чтобы он был неявным, так что синтаксис для использования общих и неосновных перегрузок выглядит одинаково (я не согласен с этим принципом, но это личное и не имеет значения здесь).

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