#c# #code-analysis #stylecop
#c# #анализ кода #stylecop
Вопрос:
Когда я запускаю анализ кода (VS2013) с набором правил «Microsoft Managed Recommend Rules», единственные предупреждения, которые я получаю для своей библиотеки классов, имеют тип CA1033: «Методы интерфейса должны вызываться дочерними типами». Но я не понимаю правила в этой ситуации:
/// An object that has a chemical formula
public interface IChemicalFormula
{
/// The chemical formula of the object
ChemicalFormula ChemicalFormula {get;}
}
public class ChemicalFormula: IChemicalFormula
{
ChemicalFormula IChemicalFormula.ChemicalFormula
{
get { return this; }
}
}
В документах рекомендуется создать защищенный метод с тем же именем, чтобы производные типы могли получить к нему доступ, но вы не можете назвать метод таким же, как и заключающий тип. Они также рекомендуют сделать класс запечатанным, но я не хочу, чтобы он был запечатан в этом случае. Настало ли время просто игнорировать это правило или есть подходящий способ справиться с этим?
Редактировать
Чтобы добавить пояснения, почему класс / интерфейс спроектирован таким образом, у меня есть другой класс, Peptide
который содержит IChemicalFormula[]
массив для хранения изменений. Не каждая модификация обязательно вытекает непосредственно из ChemicalFormula
, но они должны реализовать IChemicalFormula
интерфейс. Поэтому, если я модифицирую экземпляр пептида с какой-либо молекулой (например, H2O), тогда ChemicalFormula
класс также должен быть реализован IChemicalFormula
.
Комментарии:
1.Итак
IChemicalFormula
, представляет собой тип, который имеетChemicalFormula
? И у aChemicalFormula
есть aChemicalFormula
? Похоже, вы могли бы выбрать лучшие имена и решить свою проблему.2. Во-первых, кажется немного странным иметь в вашем интерфейсе свойство a, которое возвращает тип класса, реализующего этот интерфейс…
3. @VP У меня есть много методов расширения для IChemicalFormula, поэтому класс ChemicalFormula использует интерфейс
4. Я все еще смущен тем, что вы имеете в виду. Методы расширения — это здорово, но я хочу сказать, что если у вас есть интерфейс
A
и конкретный классB
, который его реализуетA
, этот интерфейсA
ничего не должен знать о типе классаB
, который может его реализовать, а может и не реализовать.5. Наличие свойства, которое просто возвращает
this
, на мой взгляд, на 100% бесполезно. Если у вас уже есть доступ к экземпляру, будь то с помощью имени классаChemicalFormula
или интерфейсаIChemicalFormula
, вы никогда не должны пытаться захватить экземпляр, который у вас уже есть, вызывая свойство, которое просто возвращаетthis
.
Ответ №1:
Это описание правила:
Рассмотрим базовый тип, который явно реализует метод открытого интерфейса. Тип, производный от базового типа, может получить доступ к унаследованному интерфейсному методу только через ссылку на текущий экземпляр (это в C #), который приводится к интерфейсу. Если производный тип повторно реализует (явно) унаследованный метод интерфейса, доступ к базовой реализации больше невозможен. Вызов через текущую ссылку на экземпляр вызовет производную реализацию; это вызывает рекурсию и возможное переполнение стека.
Я думаю, вам следует рассмотреть возможность оценки использования этого свойства. Хороший пример, когда TDD можно было бы использовать для определения интерфейса. Ниже приведены некоторые возможные варианты использования (и некоторые недопустимые). Я еще не уверен, чего вы намерены достичь, глядя на них.
В вашем примере, допустим, другой класс NewChemicalForumla
является производным от ChemicalForumula
и ссылается ChemicalFormula
, что это значит?
public class NewChemicalFormula: ChemicalFormula
{
public void Method()
{
Console.WriteLine("{0}", ChemicalFormula.GetType()); // Compile error
Console.WriteLine("{0}", this.ChemicalFormula.GetType()); // Effectively same as above, compile error
Console.WriteLine("{0}", ((IChemicalFormula)this).ChemicalFormula.GetType()); // Works, is that what you intend?
}
}
Теперь, находясь за пределами класса, есть две возможности:
- Когда у вас есть дескриптор производного класса:
new NewChemicalFormula().ChemicalFormula.GetType() // Error
или
// This works, is that what you intend to achieve? ((IChemicalFormula)new NewChemicalFormula()).ChemicalFormula.GetType()
- Когда у вас уже есть дескриптор IChemicalFormula. В этом случае химическая формула кажется излишней:
IChemicalFormula formula = new NewChemicalFormula(); Console.WriteLine("{0}", formula.GetType()); // Works, returns NewChemicalFormula Console.WriteLine("{0}", formula.ChemicalFormula.GetType()); // Works, returns NewChemicalFormula Console.WriteLine("{0}", formula.ChemicalFormula.Method()); // Compile error
formula.ChemicalFormula.Method()
приводит к ошибке, потому что вы должны привести его к NewChemicalFormula
, прежде чем вы сможете использовать Method()
. Просто потому, что свойство возвращается this
, это не помогает решить эту проблему.
Так что предупреждение FxCop стоит рассмотреть и оценить дизайн.
Комментарии:
1. Спасибо за подробный ответ. Я думаю, что то, что я собираюсь сделать, это просто сделать
ChemicalFormula
sealed в конце концов, это позволит избежать этих проблем, которые вы поднимаете.2. @Moop Вам не обязательно делать его запечатанным. Лучше всего написать код, как, по вашему мнению, будет использоваться это свойство, и посмотреть, решит ли это проблему, которую вы пытаетесь решить, и есть ли какие-либо альтернативы. Я пока не знаю, какой цели будет служить это свойство, поскольку оно просто возвращает сам экземпляр, т.Е.
abc.ChemicalFormula == abc
Не так ли? Если это так, то зачем это нужно.ChemicalFormula
?3. @Moop sealed просто скроет проблему в вашем дизайне. Вы должны переосмыслить то, чего вы на самом деле хотите достичь, потому что то, что у вас есть выше, не имеет смысла, как объясняли многие другие.