CA1033 со свойствами

#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 ? И у a ChemicalFormula есть a ChemicalFormula ? Похоже, вы могли бы выбрать лучшие имена и решить свою проблему.

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?
    }
}
 

Теперь, находясь за пределами класса, есть две возможности:

  1. Когда у вас есть дескриптор производного класса:
     new NewChemicalFormula().ChemicalFormula.GetType() // Error
     

    или

     // This works, is that what you intend to achieve?
    ((IChemicalFormula)new NewChemicalFormula()).ChemicalFormula.GetType()  
     
  2. Когда у вас уже есть дескриптор 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 просто скроет проблему в вашем дизайне. Вы должны переосмыслить то, чего вы на самом деле хотите достичь, потому что то, что у вас есть выше, не имеет смысла, как объясняли многие другие.