FxCop: перший погляд

вторник, 18 ноября 2008, Александр Краковецкий

Розповім про цікаву програму, яка має назву FxCop (состання версія 1.36) яку можна скачати тут.

Для тих, хто не в курсі, FxCop - це програма для статистичної перевірки коду (assemblies). Кожен поважаючий себе програміст повинен писати код правильно. А посидівши місяць-два з цією програмкою, ви просто не зможете писати неправильно.

Розглянемо простий приклад написаного класу (Visual Studio 2008 -> New Project -> Class Library -> Project4FxCopTesting -> TestClass):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Project4FxCopTest
{
    public class TestClass
    {

        public int CustomerID
        {
            get;
            set;
        }

        public string CustomerName
        {
            get;
            set;
        }

        public List CustomerAdresses
        {
            get;
            set;
        }

        public TestClass()
        {

        }
    }
}

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

Відкриваємо FxCop -> Projects -> Add Targets -> йдемо до нашого проекту -> вибираємо Project4FxCopTest.dll -> тиснемо Analyse. І що ми маємо? А маємо ми аж 6 (!) помилок:

А тепер по порядку:

  1. Assemblies should have valid strong names (дослівно - збірки повинні мати сильне ім'я) і хелп по проблемі:

    • Target: project4fxcoptest.dll (IntrospectionTargetModule)
    • Resolution: "Sign 'Project4FxCopTest.dll' with a strong name key."
    • Help: http://msdn2.microsoft.com/ms182127(VS.90).aspx (String)
    • Category: Microsoft.Design (String)
    • CheckId: CA2210 (String)
    • RuleFile: Design Rules (String)
    • Info: "Either the assembly has no strong name, an invalid one, or the strong name is valid only because of the computer configuration. The assembly should not be deployed in this state. The most common causes of this are: 1) The assembly's contents were modified after it was signed. 2) The signing process failed. 3) The assembly was delay-signed. 4) A registry key existed that allowed the check to pass (where it would not have otherwise)."

Причина: збірку потрібно підписувати.

Рішення: йдемо Properties -> Signing -> відмічаємо Sign the assembly -> в Choose a strong name key вибираємо New -> даємо ім'я -> зберігаємо. Наша dll підписана.

  1. Mark assemblies with CLSCompliantAttribute

    • Target: project4fxcoptest.dll (IntrospectionTargetModule)
    • Resolution: "Mark 'Project4FxCopTest.dll' with CLSCompliant(true) because it exposes externally visible types."
    • Help: http://msdn2.microsoft.com/ms182156(VS.90).aspx (String)
    • Category: Microsoft.Design (String)
    • CheckId: CA1014 (String)
    • RuleFile: Design Rules (String)
    • Info: "Assemblies should explicitly state their CLS compliance using the CLSCompliant attribute. An assembly without this attribute is not CLS-compliant. Assemblies, modules,and types can be CLS-compliant even if some parts of the assembly, module, or type are not CLS-compliant. The following rules apply: 1) If the element is marked CLSCompliant, any noncompliant members must have the CLSCompliant attribute present with its argument set to false. 2) A comparable CLS-compliant alternative member must be supplied for each member that is not CLS-compliant."

Проблема: збірка повинна мати інформацію про відповідність CLS.

Рішення: відкриваємо AssemblyInfo.cs і додаємо:

using System; [assembly: CLSCompliant(true)]

  1. Do not expose generic lists.

    • Target: #CustomerAdresses (IntrospectionAccessorizedTargetMember)
    • Resolution: "Change 'List' in 'TestClass.CustomerAdresses' to use Collection, ReadOnlyCollection or KeyedCollection"
    • Help: http://msdn2.microsoft.com/ms182142(VS.90).aspx (String)
    • Category: Microsoft.Design (String)
    • CheckId: CA1002 (String)
    • RuleFile: Design Rules (String)
    • Info: "Do not expose List in object models. Use Collection, ReadOnlyCollection or KeyedCollection instead. List is meant to be used from implementation, not in object model API. List is optimized for performance at the cost of long term versioning. For example, if you return List to the client code, you will not ever be able to receive notifications when client code modifies the collection."

Причина і рішення: я вже писав в коментарях про це, потрібно використовувати Collection замість List. Чесно говорячи, різниці не бачу :)

  1. Identifiers should be spelled correctly

    • Target: #CustomerAdresses (IntrospectionAccessorizedTargetMember)
    • Id: Adresses (String)
    • Resolution: "Correct the spelling of 'Adresses' in member name 'TestClass.CustomerAdresses' or remove it entirely if it represents any sort of Hungarian notation."
    • Help: http://msdn2.microsoft.com/bb264492(VS.90).aspx (String)
    • Category: Microsoft.Naming (String)
    • CheckId: CA1704 (String)
    • RuleFile: Naming Rules (String)
    • Info: "The individual words that make up an identifier should not be abbreviated and should be spelled correctly. If this rule generates a false positive on a term that should be recognized, add the word to the FxCop custom dictionary."

Причина: помилка в слові "Adresses"

Рішення: потрібно написати "Addresses"

  1. Collection properties should be read only

    • Target: #CustomerAdresses (IntrospectionAccessorizedTargetMember)
    • Resolution: "Change 'TestClass.CustomerAdresses' to be read-only by removing the property setter."
    • Help: http://msdn2.microsoft.com/ms182327(VS.90).aspx (String)
    • Category: Microsoft.Usage (String)
    • CheckId: CA2227 (String)
    • RuleFile: Usage Rules (String)
    • Info: "Properties that return collections should be read-only so that users cannot entirely replace the backing store. Users can still modify the contents of the collection by calling relevant methods on the collection. Note that the XmlSerializer class has special support for deserializing read-only collections. See the XmlSerializer overview for more information."

Причина: не можна давати доступ програмістам повністю переприсвоювати колекції.

Рішення: якщо потрібно все таки таку опцію передбачити, створіть метод SetCustomerAddresses:

void SetCustomerAddresses(Collection addresses)
{
    this.CustomerAddresses = addresses;
}

Забравши set; з пропертіса, отримали помилку:

'Project4FxCopTest.TestClass.CustomerAddresses.get' must declare a body because it is not marked abstract or extern. Automatically implemented properties must define both get and set accessors.

Скорочена версія запису проперті повинна містити і get; i set;, тому потрыбно створити private member і дописати відповідний код.

  1. Identifiers should be cased correctly

    • Target: #CustomerID (IntrospectionAccessorizedTargetMember)
    • Id: ID (String)
    • Resolution: "Correct the casing of 'ID' in member name 'TestClass.CustomerID' by changing it to 'Id'. 'Id' is an abbreviation and therefore is not subject to acronym casing guidelines."
    • Help: http://msdn2.microsoft.com/ms182240(VS.90).aspx (String)
    • Category: Microsoft.Naming (String)
    • CheckId: CA1709 (String)
    • RuleFile: Naming Rules (String)
    • Info: "Type, namespace, and member identifiers are Pascal-cased. Parameter identifiers are camel-cased. Two letter acronyms within these identifiers should be upper-cased, for example, use System.IO instead of System.Io. Acronyms of three or more letters should be Pascal-cased, for use System.Xml instead of System.XML. The pascal-casing convention capitalizes the first letter of each word, as in BackColor. The camel-casing convention formats the first letter of the first word in lowercase and capitalizes the first letter of all subsequent words, as in backgroundColor. Although it may be common practice for some two letter acronyms to not be fully capitalized, violations of this rule should not be excluded for this reason. For example, 'DbConnection',is common but incorrect; use DBConnection. A violation of this rule might be required for compatibility with existing, non-managed symbol schemes. In general, however,these symbols should not be visible outside the assembly that uses them."

Причина: невірна назва CustomerID

Рішення: потрібно писати CustomerId. Це актуально для всіх абревіатур: CustomerMsnNumber, CustomerIcqNumber, MsdnUrl тощо.

Правильний код після всіх змін має вигляд:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace Project4FxCopTest
{
    public class TestClass
    {

        public int CustomerId
        {
            get;
            set;

        }

        public string CustomerName
        {
            get;
            set;
        }

        private Collection customerAddresses;
        public Collection CustomerAddresses
        {
            get
            {
                return this.customerAddresses;
            }

        }

        public TestClass()
        {
            this.customerAddresses = new Collection();
        }
    }
}

Ось і все - після рекомпіляїї FxCop не видає помилок.

В коментарях хотілося б почути наскільки ця тема цікава і чи потрібно писати подібні пости й надалі.

Компании из статьи


Microsoft Украина


Сайт:
http://www.microsoft.com/ukr/ua/

Microsoft Украина Украинское подразделение компании Microsoft.

Ищите нас в интернетах!

Комментарии

Свежие вакансии