.Net方法概述
方法
“方法”是包含一系列语句的代码块。在 C# 中,每个执行指令都是在方法的上下文中完成的。方法在类或结构中声明,声明时,声明时需要指定访问级别、返回值、方法名称以及任何方法参数。方法参数放在括号中,并用逗号隔开。空括号表示方法不需要参数。方法可以有或没有参数,也可以有或没有返回值。
Main方法
Main 方法是控制台应用程序或窗口应用程序的入口点。Main 方法是 .exe 程序的入口点,程序控制流在该处开始和结束。Main 在类或结构内声明,Main 必须是静态的,且不应该是公用的,但不要求封闭类或结构是静态的。Main 的返回类型有两种:void 或 int。所声明的 Main 方法可以具有包含命令行实参的 string[] 形参,也可以不具有这样的形参。使用 Visual Studio 创建 Windows 窗体应用程序时,可以手动添加形参,也可以使用 Environment 类获取命令行实参。 形参读取为从零开始编制索引的命令行实参。与 C 和 C++ 不同,在 C# 中不将程序名称视为第一个命令行实参。
重载
定义一组名字相同的成员,但他们的参数数量或类型不同。
参数
对于被调用的方法,传入的变量称为“参数”。方法所接收的参数也是在一组括号中提供的,但必须指定每个参数的类型和名称。该名称不必与参数相同。
参数修饰符
无:值传递,原始数据的一份副本
out:引用传递,方法未给该参数赋值会出现编译错误
ref:引用传递,方法未给该参数赋值也不会出现编译错误
params:允许将一组可变数量的参数作为单独的逻辑参数进行传递,方法中只能有一个params,必须是方法中的最后一个参数
参数传递
out
ref
值类型:传递的是数据值的副本
值传递(默认)
引用传递
可选参数
指定参数的默认值,默认值必须在编译时确定而不能在运行时确定,只能放在方法参数的最后。
命名参数调用方法
使用命名参数可以在方法调用时更换参数的顺序。
返回值
方法可以向调用方返回值。如果返回类型(方法名称前列出的类型)不是 void,则方法可以使用 return 关键字来返回值。
返回值修饰符
无返回值:void
有返回值:返回值的类型名
方法签名
irtual:虚拟的
static:静态的
abstract:抽象的
sealed:封闭的
override:继承的
private:私有的,类本身才能访问
protected:受保护的,类本身及派生类可访问
internal:内部的,同一程序集内部类型才能访问
public:公共的,内部与外部都可以访问
访问修饰符
可选修饰符
#p#
.NET 泛型方法
泛型方法
泛型方法是使用类型参数声明的方法。
复制
static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; }
1.
2.
3.
4.
5.
6.
7.
类型推断
相同的类型推断规则也适用于静态方法以及实例方法。编译器能够根据传入的方法参数推断类型参数;它无法仅从约束或返回值推断类型参数。因此,类型推断不适用于没有参数的方法。类型推断在编译时、编译器尝试解析任何重载方法签名之前进行。编译器向共享相同名称的所有泛型方法应用类型推断逻辑。在重载解析步骤中,编译器仅包括类型推断取得成功的那些泛型方法。
复制
Swap(ref a, ref b);
1.
非泛型方法使用泛型参数
在泛型类中,非泛型方法可以访问类级别类型参数。
复制
class SampleClass<T> { void Swap(ref T lhs, ref T rhs) { } }
1.
2.
3.
4.
泛型类与泛型方法使用相同的泛型参数
如果定义的泛型方法接受与包含类相同的类型参数,编译器将生成警告 CS0693,因为在方法范围内,为内部 T 提供的参数将隐藏为外部 T 提供的参数。除了类初始化时提供的类型参数之外,如果需要灵活调用具有类型参数的泛型类方法,请考虑为方法的类型参数提供其他标识符,如下面示例中的 GenericList2<T> 所示。
复制
class GenericList<T> { // CS0693 void SampleMethod<T>() { } } class GenericList2<T> { //No warning void SampleMethod<U>() { } }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
泛型约束
使用约束对方法中的类型参数启用更专门的操作。此版本的 Swap<T> 现在称为 SwapIfGreater<T>,它只能与实现 IComparable<T> 的类型参数一起使用。
复制
void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T> { T temp; if (lhs.CompareTo(rhs) > 0) { temp = lhs; lhs = rhs; rhs = temp; } }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
方法重载
泛型方法可以使用许多类型参数进行重载。
复制
void DoWork() { } void DoWork<T>() { } void DoWork<T, U>() { }
1.
2.
3.
#p#
.Net扩展方法
扩展方法
扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。 对于用 C# 和 Visual Basic 编写的客户端代码,调用扩展方法与调用在类型中实际定义的方法之间没有明显的差异。
通常,建议您只在不得已的情况下才实现扩展方法,并谨慎地实现。 只要有可能,必须扩展现有类型的客户端代码都应该通过创建从现有类型派生的新类型来达到这一目的。
限制条件
必须定义在静态类中
必须使用this关键字对第一个参数进行修饰
每个扩展方法只能被内存中正确的实例和其所在的静态类调用
代码示例
复制
namespace DefineIMyInterface { using System; public interface IMyInterface { void MethodB(); } } namespace Extensions { using System; using DefineIMyInterface; public static class Extension { public static void MethodA(this IMyInterface myInterface, int i) { Console.WriteLine ("Extension.MethodA(this IMyInterface myInterface, int i)"); } public static void MethodA(this IMyInterface myInterface, string s) { Console.WriteLine ("Extension.MethodA(this IMyInterface myInterface, string s)"); } public static void MethodB(this IMyInterface myInterface) { Console.WriteLine ("Extension.MethodB(this IMyInterface myInterface)"); } } } namespace ExtensionMethodsDemo1 { using System; using Extensions; using DefineIMyInterface; class A : IMyInterface { public void MethodB() { Console.WriteLine("A.MethodB()"); } } class B : IMyInterface { public void MethodB() { Console.WriteLine("B.MethodB()"); } public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); } } class C : IMyInterface { public void MethodB() { Console.WriteLine("C.MethodB()"); } public void MethodA(object obj) { Console.WriteLine("C.MethodA(object obj)"); } } class ExtMethodDemo { static void Main(string[] args) { A a = new A(); B b = new B(); C c = new C(); a.MethodA(1); // Extension.MethodA(object, int) a.MethodA("hello"); // Extension.MethodA(object, string) a.MethodB(); // A.MethodB() b.MethodA(1); // B.MethodA(int) b.MethodB(); // B.MethodB() b.MethodA("hello"); // Extension.MethodA(object, string) c.MethodA(1); // C.MethodA(object) c.MethodA("hello"); // C.MethodA(object) c.MethodB(); // C.MethodB() } } }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
#p#
.NET 分部方法
分部方法
分部类或结构可以包含分部方法。 类的一个部分包含方法的签名。 可以在同一部分或另一个部分中定义可选实现。 如果未提供该实现,则会在编译时移除方法以及对方法的所有调用。
分部方法使类的某个部分的实施者能够定义方法(类似于事件)。 类的另一部分的实施者可以决定是否实现该方法。 如果未实现该方法,编译器将移除方法签名以及对该方法的所有调用。 调用该方法,包括调用中的任何计算结果,在运行时没有任何影响。 因此,分部类中的任何代码都可以随意地使用分部方法,即使未提供实现也是如此。 如果调用了未实现的方法,将不会导致编译时错误或运行时错误。
在自定义生成的代码时,分部方法特别有用。 这些方法允许保留方法名称和签名,因此生成的代码可以调用方法,而开发人员可以决定是否实现方法。 与分部类非常类似,分部方法使代码生成器创建的代码和开发人员创建的代码能够协同工作,而不会产生运行时开销。
分部方法声明由两个部分组成:定义和实现。 它们可以位于分部类的不同部分中,也可以位于同一部分中。 如果不存在实现声明,则编译器将优化定义声明和对方法的所有调用。
限制条件
分部方法声明必须以上下文关键字 partial 开头,并且方法必须返回 void。
分部方法可以有 ref 参数,但不能有 out 参数。
分部方法为隐式 private 方法,因此不能为 virtual 方法。
分部方法不能为 extern 方法,因为主体的存在确定了方法是在定义还是在实现。
分部方法可以有 static 和 unsafe 修饰符。
分部方法可以为泛型的。约束将放在定义分部方法声明上,但也可以选择重复放在实现声明上。参数和类型参数名称在实现声明和定义声明中不必相同。
您可以为已定义并实现的分部方法生成委托,但不能为已经定义但未实现的分部方法生成委托。
代码示例
复制
partial void onNameChanged(); partial void onNameChanged(){}
1.
2.