Как передать тип в метод - аргумент типа против универсальных - PullRequest
16 голосов
/ 31 июля 2009

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

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

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

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

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

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

Ответы [ 7 ]

17 голосов
/ 31 июля 2009

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

public void DoSomething<T>(T myParam);

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

string foo = "bar";

DoSomething(foo);

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

DoSomething<string>(foo);

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

5 голосов
/ 31 июля 2009

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

public static T CreateInstance<T>()

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

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

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

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

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

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

3 голосов
/ 01 августа 2009

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

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

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

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

1 голос
/ 01 августа 2009

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

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

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

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

0 голосов
/ 01 августа 2009

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

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

0 голосов
/ 31 июля 2009

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

Похоже, вы знаете, что делаете, почему некоторые автоматизированные программы знают лучше?

Ну, это не так, это предположение.

0 голосов
/ 31 июля 2009

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

См. это примечание о его подавлении. Похоже, что это нормально - подавлять.

РЕДАКТИРОВАТЬ: Теперь вот еще одна идея. Что если вы изменили его на выходной параметр и не вернули его через возвращаемую переменную? Это уберет предупреждение тогда?

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