hf

导航

 

Instance Constructors and Classes (Reference Types)

Constructor methods are always called .ctor (for constructor) in a method definition metadata table .

When constructing a reference type object, the memory allocated for the object is always zeroed out before the type’s instance constructor is called .

Instance constructors are never inherited .

If you define a class that does not explicitly define any  constructors, the C# compiler defines a default (parameterless) constructor for you whose implementation simply calls the base class’s parameterless constructor .If the class is abstract, the compiler-produced constructor is protected ; otherwise, public . If the class is static , the compiler will not emit a default constructor at all .

A class’s instance constructor must call its base class’s constructor before accessing any of the inherited fields of the base class . The C# compiler will generate a call to the default base class’s constructor  automatically if the derived class’s constructor does not explicitly invoke one of the base class’s constructors .

In a few situations, an instance of a type can be created without an instance constructor being called . Calling Object’s MemberwiseClone method allocates memory, initializes the object’s overhead fields, and then copies the source object’s bytes to the new object . The deserialization code allocates memory for the object without calling a constructor using FormatterServices type's GetUninitializedObject or GetSafeUninitializedObject methods.

You should not call any virtual methods within a constructor that can affect the object being constructed . The reason is if the virtual method is overridden in the type being  instantiated, the derived type’s implementation of the overridden method will execute, but all of the fields in the hierarchy have not been fully initialized .

The C# compiler allows the convenient syntax that lets you initialize the instance fields inline and translates this to code in the beginning of each constructor method to perform the initialization . So if you have several initialized instance fields and a lot of overloaded constructor methods, you should consider a approach to reduce the size of the generated code .

Instance Constructors and Structures (Value Types)

The CLR always allows the creation of value type instances, and there is no way to prevent a value type from being instantiated . For this reason, value types don’t actually even need to have a constructor defined within them, and the C# compiler doesn't emit default parameterless constructors for value types .

A value type’s instance constructor(include parameterless constructor) is executed only when explicitly called . C# doesn’t allow value types from defining parameterless constructors(but the CLR does ),So you cannot do inline instance field initialization in a value type .

Strictly speaking, value type fields are guaranteed to be 0/null when the value type is a field nested within a reference type . C# and other compilers that produce verifiable code ensure that all stack-based value types have their fields zeroed out or at least written to before being read.Any constructors that you do have for a value type must initialize all of the type’s fields .

In a value type’s constructor, this represents an instance of the value type itself and you can actually assign to it the result of newing up an instance of the value type, which really just  zeroes out all the fields . In a reference type’s constructor, this is considered readonly and so you cannot assign to it at all .

Type Constructors

Type constructor(private static) methods are always called .cctor (for class  constructor) in a method definition metadata table .

A type constructor can be applied to interfaces (although C# doesn’t allow this), reference types, and value types . If a type has a type constructor, it can have no more than one . In addition, type constructors never have parameters and access modifiers are not allowed .

The CLR guarantees that a type constructor executes only once per AppDomain(it's thread-safe).

Type constructors shouldn’t call a base type’s type constructor . Such a call isn’t necessary  because none of a type’s static fields is shared or inherited from its base type .

Type Constructor Performance

When compiling a method, the JIT compiler determines whether it must emit a call to execute a type constructor into the method .To decide where it should emit the call . There are two possibilities here:

  • The JIT compiler can emit the call immediately before code that would create the first instance of the type or immediately before code that accesses a noninherited field or member of the class . This is called precise semantics.
  • The JIT compiler can emit the call sometime before code first accesses a static field or a static or instance method, or invokes an instance constructor . This is called before-field-init semantics.

The before-field-init semantics is preferred since it gives the CLR a lot of freedom as to when it can call the type constructor, and the CLR takes advantage of this whenever possible to produce code that executes faster .

Compilers inform the CLR of a choice by setting the beforefieldinit flag in the row of the type definition metadata table .

When the C# compiler sees a class with static fields that use inline initialization the compiler emits the class’s type definition table entry with the BeforeFieldInit metadata flag . When the C# compiler sees a class with an explicit type constructor the compiler emits the class’s type definition table entry without the  flag .

Operator Overload Methods

Although the CLR doesn’t know anything about operators, it does specify how languages should expose operator overloads so that they can be readily consumed by code written in a different programming language . The CLR specification mandates that operator overload methods should be public and static methods .

public sealed class Complex {  
   public static Complex operator+(Complex c1, Complex c2) { ... }  
}

The compiler emits a metadata method definition entry for a method called op_*; the entry also has the specialname flag set . When language compilers see a + operator specified in source code, they look to see if one of the operand’s types defines a  specialname method called op_* whose parameters are compatible with the  operand’s types . If this method exists, the compiler emits code to call this method ,else a compilation error occurs .

The core numeric types (Int32,…) don’t define any operator overload methods . The reason is that compilers look specifically for operations on these primitive types and emit IL instructions that directly manipulate instances of these types for performance.

Operators and Programming Language Interoperability

When using languages that do not support operator overloading, the language should allow you to call the desired op_* method  directly  .

If compilers that supported  operator overloading just didn’t emit the specialname metadata flag, the rules would be a lot simpler...

Conversion Operator Methods

Conversion operators are methods that convert an object from one type to another type .

   // Implicitly constructs and returns a Rational from an Int32 ; Rational r1 = 5
   public static implicit operator Rational(Int32 num) {   
      return new Rational(num);   
   }  
   // Explicitly returns a Single from a Rational; Single s = (Single) r1
   public static explicit operator Single(Rational r) {   
      return r.ToSingle();  
   }

The metadata for the conversion operator methods looks like this:

public static Rational op_Implicit(Int32 num) 
public static Single   op_Explicit(Rational r)

C# generates code to invoke explicit conversion operators when using a cast expression; they are never invoked when using C#’s as or is operators .

Extension Methods

It allows you to define a static method that you can invoke using instance method syntax .

public static class StringBuilderExtensions { 
   public static Int32 IndexOf(this StringBuilder sb, Char value) { 
      for (Int32 index = 0; index < sb.Length; index++) 
         if (sb[index] == value) return index; 
      return -1; 
   } 
}

Rules and Guidelines

  • C# supports extension methods only; it does not offer extension properties, extension events, extension operators, and so on .
  • Extension methods(with this before their first argument)  must be declared in non-generic, non-nested, static classes .
  • To improve performance and also to avoid considering an extension method that you may not want, the C# compiler requires that you “import” extension methods (using namespace).
  • When you extend a type with an extension method, you are actually extending derived types with this method as well .
  • The compiler will bind to the instance method instead of extension method when they have a same name.

Extending Various Types with Extension Methods

Since an extension method is really the invocation of a static method, the CLR does not emit code ensuring that the value of the expression used to invoke the method is not null.

You can define extension methods for interface types , delegate types.

The Extension Attribute

In C#, when you mark a extension method, the compiler internally applies a custom attribute(ExtensionAttribute) to the method , to the static class and the assembly .So now, when compiling code that invokes an instance method that doesn’t exist, the compiler can quickly scan all the referenced assemblies to know which ones contain extension methods .

Partial Methods

Sample:

    // Tool-produced code in some source code file: 
    internal sealed partial class Base {
        // This defining-partial-method-declaration is called before changing the m_name field 
        partial void OnNameChanging(String value);
    }
    // Developer-produced code in some other source code file: 
    internal sealed partial class Base {
        // This implementing-partial-method-declaration is called before m_name is changed  
        partial void OnNameChanging(String value) {
                throw new NotImplementedException();
        }
    }

Rules and Guidelines

  • They can only be declared within a partial class or struct .
  • Partial methods must always have a return type of void, and they cannot have any parameters marked with the out modifier . These restrictions are in place because at runtime, the method may not exist .
  • The defining partial method declaration and the implementing partial method declaration must have identical signatures .
  • If there is no implementing partial method declaration, then you cannot have any code that attempts to create a delegate that refers to the partial method .
  • Partial methods are always considered to be private methods . However, the C# compiler forbids you from putting access modifiers before the partial method declaration .
posted on 2010-07-14 18:11  hf  阅读(454)  评论(0)    收藏  举报