CLR via C# 3rd - 08 - Methods
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了CLR via C# 3rd - 08 - Methods,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含19809字,纯文字阅读大概需要29分钟。
内容图文
![CLR via C# 3rd - 08 - Methods](/upload/InfoBanner/zyjiaocheng/1335/8eba0100014548d5a05d0968b16754ae.jpg)
Kinds of methods
Constructors
Type constructors
Overload operators
Type conversions (for implicit and explicit casting)
Extension methods
Partial methods.
1. 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. Any fields that the constructor doesn’t explicitly overwrite are guaranteed to have a value of 0 or null.
Unlike other methods, instance constructors are never inherited. That is, a class has only the instance constructors that the class itself defines. Since instance constructors are never inherited, you cannot apply the following modifiers to an instance constructor: virtual, new, override, sealed, or abstract. If you define a class that does not explicitly define any constructors, the C# compiler defines a default (parameterless) constructor for you whose implementation simplycalls the base class’s parameterless constructor.
For example, if you define the following class:
public class SomeType {
}
it is as though you wrote the code like this:
public class SomeType {
public SomeType() : base() { }
}
If the class is abstract, the compiler-produced default constructor has protected accessibility; otherwise, the constructor is given public accessibility. If the base class doesn’t offer a parameterless constructor, the derived class must explicitly call a base class constructor or the compiler will issue an error. If the class is static (sealed and abstract), the compiler will not emit a default constructor at all into the class definition.
In a few situations, an instance of a type can be created without an instance constructor being called. In particular, 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. Also, a constructor is usually not called when deserializing an object with the runtime serializer. The deserialization code allocates memory for the object without calling a constructor using the System.Runtime.Serialization.FormatterServices type‘s GetUninitializedObject or GetSafeUninitializedObject methods.
Important 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. Calling a virtual method would therefore result in unpredictable behavior.
The C# compiler allows the convenient syntax that lets you initialize the instance fields inline and translates this to code in the constructor method to perform the initialization. This means that you should be aware of code explosion. After this initialization code, the compiler inserts a call to the base class’s constructor, and then the compiler appends to the method the code that appears in the constructor methods. Note that the fields without the convenient syntax will be guaranteed to be initialized even though no codes exists to explicitly initialized them.
Note The compiler initializes any fields using the convenient syntax before calling a base class’s constructor to maintain the impression that these fields always have a value as the source code appearance dictates. The potential problem occurs when a base class’s constructor invokes a virtual method that calls back into a method defined by the derived class. If this happens, the fields initialized using the convenient syntax have been initialized before the virtual method is called.
If you have several initialized instance fields and a lot of overloaded constructor methods, you should consider defining the fields without the initialization, creating a single constructor that performs the common initialization, and having each constructor explicitly call the common initialization constructor. This approach will reduce the size of the generated code.
internal sealed class SomeType {
// Do not explicitly initialize the fields here
private Int32 m_x;
private String m_s;
private Double m_d;
private Byte m_b;
// This constructor sets all fields to their default.
// All of the other constructors explicitly invoke this constructor.
public SomeType() {
m_x = 5;
m_s = "Hi there";
m_d = 3.14159;
m_b = 0xff;
}
// This constructor sets all fields to their default, then changes m_x.
public SomeType(Int32 x) : this() {
m_x = x;
}
// This constructor sets all fields to their default, then changes m_s.
public SomeType(String s) : this() {
m_s = s;
}
// This constructor sets all fields to their default, then changes m_x & m_s.
public SomeType(Int32 x, String s) : this() {
m_x = x;
m_s = s;
}
}
2. Instance Constructors and Structures (Value Types)
The C# compiler doesn‘t emit default parameterless constructors for value types.
C# doesn’t allow a value type to define a parameterless constructor.
The CLR does allow you to define constructors on value types. The only way that these constructors will execute is if you write code to explicitly call one of them.
Without a parameterless constructor, a value type’s fields are always initialized to 0/null.
Note Strictly speaking, value type fields are guaranteed to be 0/null when the value type is a field nested within a reference type. However, stack-based value type fields are not guaranteed to be 0/null. For verifiability, any stack-based value type field must be written to prior to being read. If code could read a value type’s field prior to writing to the field, a security breach is possible. 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 so that a verification exception won’t be thrown at run time. For the most part, this means that you can assume that your value types have their fields initialized to 0, and you can completely ignore everything in this note.
Because C# doesn’t allow value types with parameterless constructors, compiling the following type produces the following message: "error CS0573: ‘SomeValType.m_x‘: cannot have instance field initializers in structs."
internal struct SomeValType {
// You cannot do inline instance field initialization in a value type
private Int32 m_x = 5;
}
As an alternative way to initialize all the fields of a value type, you can actually do this:
// C# allows value types to have constructors that take parameters.
public SomeValType(Int32 x) {
// Looks strange but compiles fine and initializes all fields to 0/null
this = new SomeValType();
m_x = x; // Overwrite m_x’s 0 with x
// Notice that m_y was initialized to 0.
}
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 read-only and so you cannot assign to it at all.
3. Type Constructors
In addition to instance constructors, the CLR also supports type constructors (also known as static constructors, class constructors, or type initializers).
A type constructor can be applied to interfaces (although C# doesn’t allow this), reference types, and value types.
Type constructors are used to set the initial state of a type.
By default, types don’t have a type constructor defined within them. If a type has a type constructor, it can have no more than one. In addition, type constructors never have parameters.
You’ll notice that you define type constructors just as you would parameterless instance constructors, except that you must mark them as static. Also, type constructors should always be private; C# makes them private for you automatically. In fact, if you explicitly mark a type constructor as private (or anything else) in your source code, the C# compiler issues the following error: "error CS0515: ‘SomeValType.SomeValType()‘: access modifiers are not allowed on static constructors."
Important While you can define a type constructor within a value type, you should never actually
do this because there are times when the CLR will not call a value type’s static type constructor.
Here is an example:
internal struct SomeValType {
static SomeValType() {
Console.WriteLine("This never gets displayed");
}
public Int32 m_x;
}
public sealed class Program {
public static void Main() {
SomeValType[] a = new SomeValType[10];
a[0].m_x = 123;
Console.WriteLine(a[0].m_x); // Displays 123
}
}
Note Since the CLR guarantees that a type constructor executes only once per AppDomain and is thread-safe, a type constructor is a great place to initialize any singleton objects required by the type.
Finally, if a type constructor throws an unhandled exception, the CLR considers the type to be unusable. Attempting to access any fields or methods of the type will cause a System.TypeInitializationException to be thrown.
When the C# compiler generates IL code for the type constructor, it first emits the code required to initialize the static fields followed by the explicit code contained in your type constructor method.
Important Developers occasionally ask me if there’s a way to get some code to execute when a type is unloaded. You should first know that types are unloaded only when the AppDomain unloads. When the AppDomain unloads, the object that identifies the type becomes unreachable, and the garbage collector reclaims the type object’s memory. This behavior leads many developers to believe that they could add a static Finalize method to the type, which will automatically get called when the type is unloaded. Unfortunately, the CLR doesn’t support static Finalize methods. All is not lost, however. If you want some code to execute when an AppDomain unloads, you can register a callback method with the System.AppDomain type’s DomainUnload event.
4. Type Constructor Performance
The JIT compiler determines whether it must emit a call to execute a type constructor into the method. If the JIT compiler decides to emit the call, it must decide where it should emit the call. There are two possibilities here:
1) 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 because the CLR will call the type constructor at precisely the right time.
2) 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-fieldinit semantics because the CLR guarantees only that the static constructor will run some time before the member is accessed; it could run much earlier.
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 BeforeFieldInit metadata flag.
5. Operator Overload Methods
The CLR specification mandates that operator overload methods be public and static methods. In addition, C# (and many other languages) requires that at least one of the operator method’s parameters must be the same as the type that the operator method is defined within.
Here is an example of an operator overload method defined in a C# class definition:
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_Addition; the method definition entry also has the specialname flag set, indicating that this is a “special” method.
C# Unary Operators and Their CLS-Compliant Method Names
C# Binary Operators and Their CLS-Compliant Method Names
6. Conversion Operator Methods
Conversion operators are methods that convert an object from one type to another type. You define a conversion operator method by using special syntax. The CLR specification mandates that conversion overload methods be public and static methods
public sealed class Rational
{
// Constructs a Rational from an Int32
public Rational(Int32 num) { }
// Constructs a Rational from a Single
public Rational(Single num) { }
// Convert a Rational to an Int32
public Int32 ToInt32() { return Int32.MinValue; }
// Convert a Rational to a Single
public Single ToSingle() { return Single.MinValue; }
// Implicitly constructs and returns a Rational from an Int32
public static implicit operator Rational (Int32 num)
{
return new Rational (num);
}
// Implicitly constructs and returns a Rational from a Single
public static implicit operator Rational (Single num)
{
return new Rational (num);
}
// Explicitly returns an Int32 from a Rational
public static explicit operator Int32 (Rational r)
{
return r.ToInt32();
}
//Explicitly returns a Single from a Rational
public static explicit operator Single (Rational r)
{
return r.ToSingle();
}
}
public sealed class Program
{
public static void Main()
{
Rational r1 = 5; // Implicit cast from Int32 to Rational
Rational r2 = 2.5F; // Implicit cast from Single to Rational
Int32 x = (Int32 )r1; // Explicit cast from Rational to Int32
Single s = (Single )r2; // Explicit cast from Rational to Single
}
}
Under the covers, the C# compiler detects the casts (type conversions) in the code and internally generates IL code that calls the conversion operator methods defined by the Rational type.
For the Rational type, the metadata for the four conversion operator methods looks like this:
public static Rational op_Implicit(Int32 num)
public static Rational op_Implicit(Single num)
public static Int32 op_Explicit(Rational r)
public static Single op_Explicit(Rational r)
As you can see, methods that convert an object from one type to another are always named op_Implicit or op_Explicit.
Note The two op_Explicit methods take the same parameter, a Rational. However, the methods differ by their return value, an Int32 and a Single. This is an example of two methods that differ only by their return type. The CLR fully supports the ability for a type to define multiple methods that differ only by return type. However, very few languages expose this ability. As you’re probably aware, C++, C#, Visual Basic, and Java are all examples of languages that don’t support the definition of multiple methods that differ only by their return type.
Note 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.
7. Extension Methods
To turn method into an extension method, we simply add the this keyword before the first argument.
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;
}
}
Now, when the compiler sees code like this:
Int32 index = sb.IndexOf(‘X‘);
There are some additional rules and guidelines that you should know about extension methods.
1) C# supports extension methods only; it does not offer extension properties, extension events, extension operators, and so on.
2) Extension methods (methods with this before their first argument) must be declared in non-generic, static classes. However, there is no restriction on the name of the class; you can call it whatever you want. Of course, an extension method must have at least one parameter, and only the first parameter can be marked with the this keyword.
3) The C# compiler looks only for extension methods defined in static classes that are themselves defined at the file scope. In other words, if you define the static class nested within another class, the C# compiler will emit the following message: "error CS1109: Extension method must be defined in a top-level static class; StringBuilderExtensions is a nested class."
4) Since the static classes can have any name you want, it takes the C# compiler time to find extension methods as it must look at all the file-scope static classes and scan their static methods for a match. 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. For example, if someone has defined a StringBuilderExtensions class in a Wintellect namespace, then a programmer who wants to have access to this class’s extension methods must put a using Wintellect; directive at the top of his or her source code file.
5) It is possible that multiple static classes could define the same extension method. If the compiler detects that two or more extension methods exist, then the compiler issues the following message: "error CS0121: The call is ambiguous between the following methods or properties: ‘StringBuilderExtensions. IndexOf(string, char)‘ and ‘AnotherStringBuilderExtensions. IndexOf(string, char)‘." To fix this error, you must modify your source code. Specifically, you cannot use the instance method syntax to call this static method anymore; instead you must now use the static method syntax where you explicitly indicate the name of the static class to explicitly tell the compiler which method you want to invoke.
6) You should use this feature sparingly, as not all programmers are familiar with it. For example, when you extend a type with an extension method, you are actually extending derived types with this method as well. Therefore, you should not define an extension method whose first parameter is System.Object, as this method will be callable for all expression types and this will really pollute Visual Studio’s IntelliSense window.
7) There is a potential versioning problem that exists with extension methods. If, in the future, Microsoft adds an IndexOf instance method to their StringBuilder class with the same prototype as my code is attempting to call, then when I recompile my code, the compiler will bind to Microsoft’s IndexOf instance method instead of my static IndexOf method. Because of this, my program will experience different behavior. This versioning problem is another reason why this feature should be used sparingly.
The Extension Attribute
In C#, when you mark a static method’s first parameter with the this keyword, the compiler internally applies a custom attribute to the method and this attribute is persisted in the resulting file’s metadata. The attribute is defined in the System.Core.dll assembly, and it looks like this:
// Defined in the System.Runtime.CompilerServices namespace
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
public sealed class ExtensionAttribute : Attribute {
}
In addition, this attribute is applied to the metadata for any static class that contains at least one extension method. And this attribute is also applied to the metadata for any assembly that contains at least one static class that contains an extension method. 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. Then it can scan only these assemblies for static classes that contain extension methods, and it can scan just the extension methods for potential matches to compile the code as quickly as possible.
原文:http://www.cnblogs.com/junier/p/3583808.html
内容总结
以上是互联网集市为您收集整理的CLR via C# 3rd - 08 - Methods全部内容,希望文章能够帮你解决CLR via C# 3rd - 08 - Methods所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。