[收藏]C# Coding Standard from 《OReilly.Programming.dot.NET.Components.2nd.Edition》

/*程序员第八期有此文章,并且请了各位专家来点评,很不错。不过,该文翻译稍微有些问题,共享英文原版,想看者看。*/

Appendix E. C# Coding Standard

A comprehensive coding standard is essential for a successful product delivery. The standard helps in enforcing best practices and avoiding pitfalls, and makes knowledge dissemination across the team easier. Traditionally, coding standards are thick, laborious documents, spanning hundreds of pages and detailing the rationale behind every directive. While these are still better than no standard at all, such efforts are usually indigestible by the average developer. In contrast, the C# coding standard presented in this appendix is very thin on the "why" and very detailed on the "what" and the "how." I believe that while fully understanding every insight that goes into a particular programming decision may require reading books such as this and even years of experience, applying the standard should not. When absorbing a new developer into your team, you should be able to simply point him or her at the standard and say: "Read this first." Being able to comply with a good standard should come before fully understanding and appreciating itthat should come over time, with experience. The coding standard presented next is based on this book, and it captures its best practices, dos and don'ts, helper classes, pitfalls, guidelines, and recommendations, as well as naming conventions and styles, project settings and structure, and framework-specific guidelines. Since I published this standard, it has become the de facto industry standard for C# and .NET development.

Naming Conventions and Styles

  1. Use Pascal casing for type and method names and constants:

    public class SomeClass
    {
       const int DefaultSize = 100;
       public SomeMethod( )
       {}
    }
    

  2. Use camel casing for local variable names and method arguments:

    int number;
    void MyMethod(int someNumber)
    {}
    

  3. Prefix interface names with I:

    interface IMyInterface
    {..}
    

  4. Prefix private member variables with m_.

  5. Suffix custom attribute classes with Attribute.

  6. Suffix custom exception classes with Exception.

  7. Name methods using verb/object pairs, such as ShowDialog( ).

  8. Methods with return values should have names describing the values returned, such as GetObjectState( ).

  9. Use descriptive variable names.

    1. Avoid single-character variable names, such as i or t. Use index or temp instead.

    2. Avoid using Hungarian notation for public or protected members.

    3. Avoid abbreviating words (such as num instead of number).

  10. Always use C# predefined types, rather than the aliases in the System namespace. For example:

    object NOT Object
    string NOT String
    intNOT Int32
    

  11. With generics, use capital letters for types. Reserve suffixing Type for when dealing with the .NET type Type:

    //Correct:
    public class LinkedList<K,T>
    {...}
    //Avoid:
    public class LinkedList<KeyType,DataType>
    {...}
    

  12. Use meaningful namespace names, such as the product name or the company name.

  13. Avoid fully qualified type names. Use the using statement instead.

  14. Avoid putting a using statement inside a namespace.

  15. Group all framework namespaces together and put custom or third-party namespaces underneath:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using MyCompany;
    using MyControls;
    

  16. Use delegate inference instead of explicit delegate instantiation:

    delegate void SomeDelegate( );
    public void SomeMethod( )
    {...}
    SomeDelegate someDelegate = SomeMethod;
    

  17. Maintain strict indentation. Do not use tabs or nonstandard indentation, such as one space. Recommended values are three or four spaces.

  18. Indent comments at the same level of indentation as the code that you are documenting.

  19. All comments should pass spellchecking. Misspelled comments indicate sloppy development.

  20. All member variables should be declared at the top, with one line separating them from the properties or methods:

    public class MyClass
    {
       int m_Number;
       string m_Name;
    
       public void SomeMethod1( )
       {}
       public void SomeMethod2( )
       {}
    }
    

  21. Declare a local variable as close as possible to its first use.

  22. A filename should reflect the class it contains.

  23. When using partial types and allocating a part per file, name each file after the logical part that part plays. For example:

    //In MyClass.cs
    public partial class MyClass
    {...}
    //In MyClass.Designer.cs
    public partial class MyClass
    {...}
    

  24. Always place an open curly brace ({) in a new line.

  25. With anonymous methods, mimic the code layout of a regular method, aligned with the anonymous delegate declaration (this complies with placing an open curly brace in a new line):

    delegate void SomeDelegate(string someString);
    //Correct:
    public void InvokeMethod( )
    {
       SomeDelegate someDelegate = delegate(string name)
                                   {
                                      MessageBox.Show(name);
                                   };
       someDelegate("Juval");
    }
    //Avoid
    public void InvokeMethod( )
    {
       SomeDelegate someDelegate = delegate(string name){MessageBox.Show(name);};
       someDelegate("Juval");
    }
    

  26. Use empty parentheses on parameter-less anonymous methods. Omit the parentheses only if the anonymous method could have been used on any delegate:

    delegate void SomeDelegate( );
    //Correct
    SomeDelegate someDelegate1 = delegate( )
                                 {
                                   MessageBox.Show("Hello");
                                 };
    //Avoid
    SomeDelegate someDelegate1 = delegate
                                 {
                                    MessageBox.Show("Hello");
                                 };
    

Coding Practices

  1. Avoid putting multiple classes in a single file.

  2. A single file should contribute types to only a single namespace. Avoid having multiple namespaces in the same file.

  3. Avoid files with more than 500 lines (excluding machine-generated code).

  4. Avoid methods with more than 25 lines.

  5. Avoid methods with more than five arguments. Use structures for passing multiple arguments.

  6. Lines should not exceed 80 characters.

  7. Do not manually edit any machine-generated code.

    1. If modifying machine-generated code, modify the format and style to match this coding standard.

    2. Use partial classes whenever possible to factor out the maintained portions.

  8. Avoid comments that explain the obvious. Code should be self-explanatory. Good code with readable variable and method names should not require comments.

  9. Document only operational assumptions, algorithm insights, and so on.

  10. Avoid method-level documentation.

    1. Use extensive external documentation for API documentation.

    2. Use method-level comments only as tool tips for other developers.

  11. With the exception of zero and one, never hardcode a numeric value; always declare a constant instead.

  12. Use the const directive only on natural constants, such as the number of days of the week.

  13. Avoid using const on read-only variables. For that, use the readonly directive:

    public class MyClass
    {
       public const int DaysInWeek = 7;
       public readonlyint Number;
       public MyClass(int someValue)
       {
          Number = someValue;
       }
    }
    

  14. Assert every assumption. On average, every fifth line is an assertion:

    using System.Diagnostics;
    
    object GetObject( )
    {...}
    
    object someObject = GetObject( );
    Debug.Assert(someObject != null);
    

  15. Every line of code should be walked through in a "white box" testing manner.

  16. Catch only exceptions for which you have explicit handling.

  17. In a catch statement that throws an exception, always throw the original exception (or another exception constructed from the original exception) to maintain the stack location of the original error:

    catch(Exception exception)
    {  
       MessageBox.Show(exception.Message);
       throw;  //Same as throw exception;
    }
    

  18. Avoid error code as method return values.

  19. Avoid defining custom exception classes.

  20. When defining custom exceptions:

    1. Derive the custom exception from Exception.

    2. Provide custom serialization.

  21. Avoid multiple Main( ) methods in a single assembly.

  22. Make only the most necessary types public; mark others as internal.

  23. Avoid friend assemblies, as they increase interassembly coupling.

  24. Avoid code that relies on an assembly running from a particular location.

  25. Minimize code in application assemblies (i.e., EXE client assemblies). Use class libraries instead to contain business logic.

  26. Avoid providing explicit values for enums:

    //Correct
    public enum Color
    {
       Red,Green,Blue
    }
    //Avoid
    public enum Color
    {
       Red = 1,Green = 2,Blue = 3
    }
    

  27. Avoid specifying a type for an enum:

    //Avoid
    public enum Color : long
    {
       Red,Green,Blue
    }
    

  28. Always use a curly brace scope in an if statement, even if it contains a single statement.

  29. Avoid using the trinary conditional operator.

  30. Avoid function calls in Boolean conditional statements. Assign into local variables and check on them:

    bool IsEverythingOK( )
    {...}
    //Avoid:
    if(IsEverythingOK( ))
    {...}
    //Correct:
    bool ok = IsEverythingOK( );
    if(ok)
    {...}
    

  31. Always use zero-based arrays.

  32. Always explicitly initialize an array of reference types:

    public class MyClass
    {}
    const int ArrraySize = 100;
    MyClass[] array = new MyClass[ArrraySize];
    for(int index = 0; index < array.Length; index++)
    {
       array[index] = new MyClass( );
    }
    

  33. Do not provide public or protected member variables. Use properties instead.

  34. Avoid using the new inheritance qualifier. Use override instead.

  35. Always mark public and protected methods as virtual in a non-sealed class.

  36. Never use unsafe code, except when using interop.

  37. Avoid explicit casting. Use the as operator to defensively cast to a type:

    Dog dog = new GermanShepherd( );
    GermanShepherd shepherd = dog asGermanShepherd;
    if(shepherd != null)
    {...}
    

  38. Always check a delegate for null before invoking it.

  39. Do not provide public event member variables. Use event accessors instead.

  40. Avoid defining event-handling delegates. Use GenericEventHandler instead.

  41. Avoid raising events explicitly. Use EventsHelper to publish events defensively.

  42. Always use interfaces.

  43. Classes and interfaces should have at least a 2:1 ratio of methods to properties.

  44. Avoid interfaces with one member.

  45. Strive to have three to five members per interface.

  46. Do not have more than 20 members per interface. The practical limit is probably 12.

  47. Avoid events as interface members.

  48. When using abstract classes, offer an interface as well.

  49. Expose interfaces on class hierarchies.

  50. Prefer using explicit interface implementation.

  51. Never assume a type supports an interface. Defensively query for that interface:

    SomeType obj1;
    IMyInterface obj2;
    
    /* Some code to initialize obj1, then: */
    obj2 = obj1 as IMyInterface;
    if(obj2 != null)
    {
       obj2.Method1( );
    }
    else
    {
       //Handle error in expected interface
    } 
    

  52. Never hardcode strings that will be presented to end users. Use resources instead.

  53. Never hardcode strings that might change based on deployment, such as connection strings.

  54. Use String.Empty instead of "":

    //Avoid
    string name = "";
    
    //Correct
    string name = String.Empty;
    

  55. When building a long string, use StringBuilder, not string.

  56. Avoid providing methods on structures.

    1. Parameterized constructors are encouraged.

    2. You can overload operators.

  57. Always provide a static constructor when providing static member variables.

  58. Do not use late-binding invocation when early binding is possible.

  59. Use application logging and tracing.

  60. Never use goto, except in a switch statement fall-through.

  61. Always have a default case in a switch statement that asserts:

    int number = SomeMethod( );
    switch(number)
    {
       case 1:
      Trace.WriteLine("Case 1:");
      break;
       case 2:
      Trace.WriteLine("Case 2:");
      break;
       default:
       Debug.Assert(false);
      break;
    }
    

  62. Do not use the this reference unless invoking another constructor from within a constructor:

    //Example of proper use of 'this'
    public class MyClass
    {
       public MyClass(string message)
       {}
       public MyClass( ) : this("Hello")
       {}
    }
    

  63. Do not use the base word to access base class members unless you wish to resolve a conflict with a subclass member of the same name or when invoking a base class constructor:

    //Example of proper use of 'base'
    public class Dog
    {
       public Dog(string name)
       {}
       virtual public void Bark(int howLong)
       {}
    }
    public class GermanShepherd : Dog
    {
       public GermanShepherd(string name) : base(name)
       {}
       override public void Bark(int howLong)
       {
          base.Bark(howLong); 
       }
    }
    

  64. Do not use GC.AddMemoryPressure( ).

  65. Do not rely on HandleCollector.

  66. Implement Dispose( ) and Finalize( ) methods based on the template in Chapter 4.

  67. Always run code unchecked by default (for the sake of performance), but explicitly in checked mode for overflow- or underflow-prone operations:

    int CalcPower(int number,int power)
    {
       int result = 1;
       for(int count = 1;count <= power;count++)
       {
         checked
         {
            result *= number;
         }
       }
       return result;
    }
    

  68. Avoid explicit code exclusion of method calls (#if...#endif). Use conditional methods instead:

    public class MyClass
    {
       [Conditional("MySpecialCondition")]
       public void MyMethod( )
       {}
    }
    

  69. Avoid casting to and from System.Object in code that uses generics. Use constraints or the as operator instead:

    class SomeClass
    {}
    //Avoid:
    class MyClass<T>
    {
       void SomeMethod(T t)
       {
         object temp = t;
         SomeClass obj = (SomeClass)temp;
       }
    }
    //Correct:
    class MyClass<T> where T : SomeClass
    {  
       void SomeMethod(T t)  
       {
          SomeClass obj = t;  
       }
    }
    

  70. Do not define constraints in generic interfaces. Interface-level constraints can often be replaced by strong typing:

    public class Customer
    {...}
    //Avoid:
    public interface IList<T> where T : Customer
    {...}
    //Correct:
    public interface ICustomerList : IList<Customer>
    {...}
    

  71. Do not define method-specific constraints in interfaces.

  72. If a class or a method offers both generic and non-generic flavors, always prefer using the generics flavor.

  73. When implementing a generic interface that derived from an equivalent non-generic interface (such as IEnumerable<T>), use explicit interface implementation on all methods, and implement the non-generic methods by delegating to the generic ones:

        class MyCollection<T> : IEnumerable<T>
        {
           IEnumerator<T> IEnumerable<T>.GetEnumerator()
           {...}
           IEnumerator IEnumerable.GetEnumerator()
           {
              IEnumerable<T> enumerable = this;
              return enumerable.GetEnumerator();
           }
        }
    

Project Settings and Project Structure

  1. Always build your projects with Warning Level 4 (see Figure E-1).

  2. Treat warnings as errors in the Release build (note that this is not the default of Visual Studio). Although it is optional, this standard recommends treating warnings as errors in Debug builds as well.

  3. Avoid suppressing specific compiler warnings.

  4. Always explicitly state your supported runtime versions in the application configuration file:

    <?xml version="1.0"?>
    <configuration>
       <startup>
          <supportedRuntime version="v2.0.5500.0"/>
          <supportedRuntime version="v1.1.5000.0"/>
       </startup>
    </configuration>
    

    Figure E-1. The project Build pane


  5. Avoid explicit custom version redirection and binding to CLR assemblies.

  6. Avoid explicit preprocessor definitions (#define). Use the project settings for defining conditional compilation constants.

  7. Do not put any logic inside AssemblyInfo.cs.

  8. Do not put any assembly attributes in any file other than AssemblyInfo.cs.

  9. Populate all fields in AssemblyInfo.cs, such as company name, description, and copyright notice.

  10. All assembly references should use relative paths.

  11. Disallow cyclic references between assemblies.

  12. Avoid multi-module assemblies.

  13. Avoid tampering with exception handling using the Exception window (Debug Exceptions).

  14. Strive to use uniform version numbers on all assemblies and clients in the same logical application (typically, a solution). Use the SolutionInfo.cs technique from Chapter 5 to automate.

  15. Name your Visual Studio 2005 application configuration file App.config, and include it in the project.

  16. Modify the Visual Studio 2005 default project structure to your project's standard layout, and apply a uniform structure for project folders and files.

  17. A release build should contain debug symbols (see Figure E-2).

    Figure E-2. The Advanced Build Settings dialog


  18. Always sign your assemblies, including the client applications.

  19. Use password-protected keys.

 

Framework-Specific Guidelines

Multithreading
  1. Use synchronization domains. Avoid manual synchronization, because that often leads to deadlocks and race conditions.

  2. Never call outside your synchronization domain.

  3. Manage asynchronous call completion on a callback method. Do not wait, poll, or block for completion.

  4. Always name your threads:

    Thread currentThread = Thread.CurrentThread;
    string threadName = "Main UI Thread";
    currentThread.Name = threadName;
    

    The name is traced in the debugger Threads window, making debug sessions more productive.

  5. Do not call Suspend( ) or Resume( ) on a thread.

  6. Do not call Thread.Sleep( ), except in the following conditions:

    1. Thread.Sleep(0) is an acceptable optimization technique to force a context switch.

    2. Thread.Sleep( ) is acceptable in testing or simulation code.

  7. Do not call THRead.SpinWait( ).

  8. Do not call Thread.Abort( ) to terminate threads. Use a synchronization object instead to signal the thread to terminate.

  9. Avoid explicitly setting the thread priority to control execution. You can set the thread priority based on task semantics (such as ThreadPriority.BelowNormal for a screensaver).

  10. Do not read the value of the ThreadState property. Use Thread.IsAlive( ) to determine whether the thread is dead or alive.

  11. Do not rely on setting the thread type to background thread for application shutdown. Use a watchdog or other monitoring entity to deterministically kill threads.

  12. Do not use the thread local storage unless thread affinity is guaranteed.

  13. Do not call Thread.MemoryBarrier( ).

  14. Never call Thread.Join( ) without checking that you are not joining your own thread:

    void WaitForThreadToDie(Thread thread)
    {
       Debug.Assert(Thread.CurrentThread.ManagedThreadId != thread.ManagedThreadId);
       thread.Join( );
    }
    

  15. Always use the lock( ) statement rather than explicit Monitor manipulation.

  16. Always encapsulate the lock( ) statement inside the object it protects:

    public class MyClass
    {  
       public void DoSomething( )  
       { 
          lock(this) 
          {...}
       }
    }
    

  17. You can use synchronized methods instead of writing the lock( ) statement yourself.

  18. Avoid fragmented locking.

  19. Avoid using a Monitor to wait or pulse objects. Use manual or auto-reset events instead.

  20. Do not use volatile variables. Lock your object or fields instead to guarantee deterministic and thread-safe access. Do not use THRead.VolatileRead( ), Thread.VolatileWrite( ), or the volatile modifier.

  21. Avoid increasing the maximum number of threads in the thread pool.

  22. Never stack lock( ) statements, because that does not provide atomic locking:

    MyClass obj1 = new MyClass( );
    MyClass obj2 = new MyClass( );
    MyClass obj3 = new MyClass( );
    
    //Do not stack lock statements
    lock(obj1)
    lock(obj2)
    lock(obj3)
    {
       obj1.DoSomething( );
       obj2.DoSomething( );
       obj3.DoSomething( );
    }
    

    Use WaitHandle.WaitAll( ) instead.

Serialization
  1. Prefer the binary formatter.

  2. Mark serialization event-handling methods as private.

  3. Use the generic IGenericFormatter interface.

  4. Always mark non-sealed classes as serializable.

  5. When implementing IDeserializationCallback on a non-sealed class, make sure to do so in a way that allows subclasses to call the base class implementation of OnDeserialization( ).

  6. Always mark unserializable member variables as non-serializable.

  7. Always mark delegates on a serialized class as non-serializable fields:

    [Serializable]
    public class MyClass
    {
       [field:NonSerialized]
       public event EventHandler MyEvent;
    }
    

Remoting
  1. Prefer administrative configuration to programmatic configuration.

  2. Always implement IDisposable on single-call objects.

  3. Always prefer a TCP channel and a binary format when using remoting, unless a firewall is present.

  4. Always provide a null lease for a singleton object:

    public class MySingleton : MarshalByRefObject
    {
       public override object InitializeLifetimeService( )
       {
          return null;
       }
    }
    

  5. Always provide a sponsor for a client-activated object. The sponsor should return the initial lease time.

  6. Always unregister the sponsor on client application shutdown.

  7. Always put remote objects in class libraries.

  8. Avoid using SoapSuds.exe.

  9. Avoid hosting in IIS.

  10. Avoid using uni-directional channels.

  11. Always load a remoting configuration file in Main( ), even if the file is empty and the application does not use remoting:

    static void Main( )
    {
       RemotingConfigurationEx.Configure( );
       /* Rest of Main( )  */
    }
    

  12. Avoid using Activator.GetObject( ) and Activator.CreateInstance( ) for remote object activation. Use new instead.

  13. Always register port 0 on the client side, to allow callbacks.

  14. Always elevate type filtering to Full on both client and host, to allow callbacks.

Security
  1. Always demand your own strong name on assemblies and components that are private to the application, but are public (so that only you can use them):

    public class PublicKeys
    {
       public const string MyCompany = "1234567894800000940000000602000000240000"+
                                       "52534131000400000100010007D1FA57C4AED9F0"+
                                       "A32E84AA0FAEFD0DE9E8FD6AEC8F87FB03766C83"+
                                       "4C99921EB23BE79AD9D5DCC1DD9AD23613210290"+
                                       "0B723CF980957FC4E177108FC607774F29E8320E"+
                                       "92EA05ECE4E821C0A5EFE8F1645C4C0C93C1AB99"+
                                       "285D622CAA652C1DFAD63D745D6F2DE5F17E5EAF"+
                                       "0FC4963D261C8A12436518206DC093344D5AD293";
    }
    
    [StrongNameIdentityPermission(SecurityAction.LinkDemand,
                                  PublicKey = PublicKeys.MyCompany)]
    public class MyClass
    {...}
    

  2. Apply encryption and security protection on application configuration files.

  3. When importing an interop method, assert unmanaged code permission and demand appropriate permission instead:

    [DllImport("user32",EntryPoint="MessageBoxA")]
    private static extern int Show(IntPtr handle,string text,string caption,
                                                                         int msgType);
    [SecurityPermission(SecurityAction.Assert,UnmanagedCode = true)]
    [UIPermission(SecurityAction.Demand, 
                                      Window = UIPermissionWindow.SafeTopLevelWindows)]
    public static void Show(string text,string caption)
    {  
       Show(IntPtr.Zero,text,caption,0);
    }
    

  4. Do not suppress unmanaged code access via the SuppressUnmanagedCodeSecurity attribute.

  5. Do not use the /unsafe switch of TlbImp.exe. Wrap the RCW in managed code so that you can assert and demand permissions declaratively on the wrapper.

  6. On server machines, deploy a code access security policy that grants only Microsoft, ECMA, and self (identified by a strong name) full trust. Code originating from anywhere else is implicitly granted nothing.

  7. On client machines, deploy a security policy that grants client application only the permissions to execute, to call back the server, and to potentially display user interface. When not using ClickOnce, client application should be identified by a strong name in the code groups.

  8. To counter a luring attack, always refuse at the assembly level all permissions not required to perform the task at hand:

    [assembly:UIPermission(SecurityAction.RequestRefuse,
                           Window=UIPermissionWindow.AllWindows)]
    

  9. Always set the principal policy in every Main( ) method to Windows:

    public class MyClass
    {
       static void Main( )
       {
          AppDomain currentDomain = AppDomain.CurrentDomain;
          currentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
       }
       //other methods
    }
    

  10. Never assert a permission without demanding a different permission in its place.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值