C#方法

简介:

C#方法

1:实例构造器和类

2:实例构造器和结构

3:类型构造器

4:操作符重载方法

5:转换操作符方法

6:扩展方法

7:部分方法

 

这篇博客的内容基本上是CLR via C#中第八章 “方法”的大致内容,如果我说得不对,欢迎大家搬砖头

 

1:实例构造器和类

构造器是允许将类型的实例初始化为良好状态的一种特殊方法,创建一个引用类型的实例时,先为实例的数据字段分配内存,然后初始化对象的附加字段(类型对象指针和同步索引),最后调用构造函数来设置对象的初始状态。构造函数不能被继承,所以不能被virtual、new、override、sealed和abstract修饰,若没有显示定义任何构造函数,编译器将定义一个无参的public构造函数,但若是抽象类,编译器将定义一个无参的protected的构造函数

 

创建一个类的实例并不一定非要调用构造函数。

1:使用ObjectMemberwiseClone()方法。他的作用就是创建当前 System.Object 的浅表副本,内部工作机制是分配内存,初始化对象的附加字段(类型对象指针和同步索引),然后将源对象的字节数据复制到新对象中。从下面的代码可以看出MemberwiseClone()实现了对象复制,而不是简单的对象引用。

复制代码
    class Program
{
public int X;
static void Main(string[] args)
{
Program p = new Program();
p.X = 1;
Program q = (Program)p.MemberwiseClone();
Program z = p;
p.X = 2;
Console.WriteLine("p.X=" + p.X);//输出:p.X=2
Console.WriteLine("q.X=" + q.X);//输出:q.X=1
Console.WriteLine("z.X=" + z.X);//输出:z.X=2
Console.Read();
}
}
复制代码

2:反序列化。在网络编程的时候,经常将一个对象序列化成二进制,然后传输出去,接收端反序列化成原来的对象,反序列化使用的是类

System.Runtime.Serialization.FormatterServices的方法public static object GetUninitializedObject(Type type)或

public static object GetSafeUninitializedObject(Type type)分配内存,而在这两个方法内部没有调用要被反序列化对象的构造函数。

字段的初始化代码会被编译器自动添加到相应的构造函数中,非静态字段的初始化代码会自动加到实例构造函数中,静态字段的初始化代码则添加到静态构造函数中,如果你的代码中有多个字段被初始化,还有多个构造函数的话,初始化代码在每个构造函数中都会有一份,这无疑会让你的生成文件(如DLL,EXE文件)变大。

证明这一点的代码如下:

复制代码
   public class SomeType
{
public int x=1,y=2,z=3;
public SomeType() { }
public SomeType(int x, int y)
{
this.x = x;
this.y = y;
}
public SomeType(int x)
{
this.x = x;
}
}
//经过IL反编译,方法SomeType(int x, int y)代码如下
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 x,
int32 y) cil managed
{
// 代码大小 45 (0x2d)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: stfld int32 MyTest.SomeType::x
IL_0007: ldarg.0
IL_0008: ldc.i4.2
IL_0009: stfld int32 MyTest.SomeType::y
IL_000e: ldarg.0
IL_000f: ldc.i4.3
IL_0010: stfld int32 MyTest.SomeType::z
IL_0015: ldarg.0
IL_0016: call instance void [mscorlib]System.Object::.ctor()
IL_001b: nop
IL_001c: nop
IL_001d: ldarg.0
IL_001e: ldarg.1
IL_001f: stfld int32 MyTest.SomeType::x
IL_0024: ldarg.0
IL_0025: ldarg.2
IL_0026: stfld int32 MyTest.SomeType::y
IL_002b: nop
IL_002c: ret
} // end of method SomeType::.ctor
复制代码

先执行了初始化代码,其他构造函数都包含了初始化代码,然后在执行构造函数中的赋值代码,要解决这个代码膨胀问题,方法很简单,把初始化代码写在无参构造函数中,让其他构造函数调用。

 

2:实例构造器和结构

值类型的工作方式与引用类型截然不同,值类型其实并不需要定义构造函数,地球人根本阻止不了值类型实例化,编译器根本不会生产默认无参构造函数,如果你显示声明无参构造函数,编译根本通过不了,报错“结构不能包含显式的无参数构造函数”。由于值类型存在栈中,根本不需要对堆中的数据进行引用,所以我们可以在定义的时候就直接赋值,(int i=0;string s=”a”;Point p;p.X=2;)他根本不需要new,new当然是可以的,调用构造函数会初始化所有的字段成相应类型的默认值,实例如下:

复制代码
   public struct Point
{
public int x, y;
//public Point() { }//错:结构不能包含显式的无参数构造函数
//public int z = 4;//错:结构中不能有实例字段初始值设定项
//public Point(int x) //在控制返回调用方之前,字段“Point.y”必须被完全赋值,要解决这个问题就手动给y加一个默认值吧,
//你也可以加this=new Point();将所有的字段的值初始化为0或null
//{
// this.x = x;
//}
public void Test()
{
Point p;
p.x = 1;
p.y = 2;
Console.WriteLine(p.x+","+p.y);//输出1,2
}
}
复制代码

结构的特点:

   1:不能显示定义无参构造函数

   2:不能在定义字段的时候初始化

   3:声明有参构造函数的时候,要初始化所有的字段

 

3:类型构造器

实例构造器就是静态构造函数,他的作用是设置类型的初始化状态,静态构造函数只能有一个,且是无参的,不能有访问修饰符修饰,默认就是private,由编译器调用执行。实例如下:

复制代码
public class SomeType
{
public static int x = 520;
static SomeType()
{
x = 112;
}
}

//用.NET reflector查看源码,如下
public class SomeType
{
public static int x ;
static SomeType()
{
x = 520;
x = 0x70;
}
public SomeType() { }
}
复制代码

在定义静态字段并初始化,编译器会自动生成一个类型构造器(静态构造函数),并将静态字段的初始化代码插在类型构造器的前面,从上面的代码可以看出,定义时初始化和在类型构造器中初始化只需要一个即可,还有112的16进制为0x70,所以在代码中看到16进制也不用大惊小怪,根本不涉及性能问题,若定义了静态字段,但没有初始化任何一个,编译器是不会生成类型构造器的。但是静态字段还是会被初始化,其实不管是静态的还是非静态的字段都是会被编译器自动初始化的,int类型的初始化为0;bool:False;string:null,这就是为什么你在实例化实体的时候,有些字段你没有初始化,却不会报错,而且你知道没有初始化的字符串的值就是null,也就是说编译器会帮你初始化你没有初始化的字段,然而在方法中定义的局部变量是需要自己初始化的,如果你没有初始化,会报一个错误“使用了未赋值的局部变量X”。

 

4:操作符重载方法

要想实现操作符重载,只需要保证以下两点,其他的话都是浮云:

1:操作符重载方法必须是public和static方法

2:操作符重载方法至少有一个参数的类型与当前定义这个方法的类型相同。之所以是要这个条件是为了使编译器在合理的时间内找到要绑定的操作方法,实例如下

 

复制代码
 public class Complex
{
public int data;
public Complex(int data)
{
this.data = data;
}
public static Complex operator +(Complex c1, Complex c2)
{
return new Complex(c1.data+c2.data);
}

public void Test()
{
Complex c1 = new Complex(1);
Complex c2 = new Complex(2);
Complex c3 = c1 + c2;
Console.WriteLine(c3.data);//输出3
}
}
复制代码

 

5:转换操作符方法

要实现转换操作符方法,条件和操作符重载方法的条件是一样的,实例如下:

复制代码
 public class Rational
{
public Rational(int data)
{
this.data = data;
}

public Rational(char data)
{
this.data = (int)data;
}

//隐式类型转换:int->Rational
public static implicit operator Rational(int data)
{
return new Rational(data);
}

//隐式类型转换:char->Rational
public static implicit operator Rational(char data)
{
return new Rational(data);
}

//显示类型转换 Rational->int
public static explicit operator int(Rational val)
{
return val.data;
}

//显示类型转换 Rational->char
public static explicit operator char(Rational val)
{
return Convert.ToChar(val.data);
}

public void Test()
{
Rational r1 = 1;//将int类型隐式转换成Rational
Rational r2 = '2';//将char类型隐式转换成Rational
int i = (int)r1;//将Rational类型显示转换成int
char c = (char)r2;//将Rational类型显示转换成char
Console.WriteLine("i=" + i);//输出:i=1
Console.WriteLine("c=" + c);//输出:c=2
}

int data;
}
复制代码

隐式和显示类型转换的实现原理就这么简单,在C++中隐式类型转换根本不需要你写代码,只要有相应的public构造函数就可以了,如int转换成Rational,只需要有构造函数public Rational(int data)就可以了,如Rational r=1;编译器会尽一切努力寻找将int类型转换成Rational的方法,当它发现这个构造函数,他说都不说就帮你进行转换了,就因为这样有时候非常坑爹,你一个int类型无缘无故的就变成Rational了,而你却根本不知道怎么回事,有时候为了解决这个问题,还得自己定义一个类(Uint)来封装int,然后构造函数改成Rational(Uint data),C#就没有这个问题,当然你要想实现隐式类型转换就自己写代码吧。

 

6:扩展方法

实现扩展方法的条件:

1:定义扩展方法的类必须是非泛型静态类

2:这个类必须有自己的作用域,即不能是内部类

3:方法必须是public和static

4:方法的第一个参数必须用this修饰,第一个参数就是你要扩展的类型,实例如下:

复制代码
 public static class StringExtensions
{
public static int ToInt(this string s)
{
return Convert.ToInt32(s);
}

public void Test()
{
string s = "2";
Console.WriteLine(s.ToInt());
}
}
复制代码

 

7:部分方法

你懂的


  作者:陈太汉

  博客:http://www.cnblogs.com/hlxs/

 


本文转自啊汉博客园博客,原文链接:http://www.cnblogs.com/hlxs/archive/2011/12/10/2283400.html

 

目录
相关文章
|
5月前
|
Java 调度 C#
C#学习系列相关之多线程(一)----常用多线程方法总结
C#学习系列相关之多线程(一)----常用多线程方法总结
|
5月前
|
C#
C#学习相关系列之数组---常用方法使用(二)
C#学习相关系列之数组---常用方法使用(二)
|
14天前
|
开发框架 .NET 程序员
C# 去掉字符串最后一个字符的 4 种方法
在实际业务中,我们经常会遇到在循环中拼接字符串的场景,循环结束之后拼接得到的字符串的最后一个字符往往需要去掉,看看 C# 提供了哪4种方法可以高效去掉字符串的最后一个字符
|
4月前
|
数据采集 数据可视化 测试技术
C#生成Selenium测试报告:实用方法与技巧
在C#中使用Selenium进行自动化测试时,结合代理IP和ExtentReports能增强测试安全性和报告质量。安装必备工具如Selenium WebDriver、NUnit和ExtentReports。在测试设置中,配置代理(如亿牛云爬虫代理)以隐藏IP,通过ChromeOptions定制UserAgent,并添加Cookie。测试代码示例展示了如何打开网页、执行搜索并生成详细的测试报告。使用ExtentReports可创建可视化测试结果,便于团队分析。
C#生成Selenium测试报告:实用方法与技巧
|
1月前
|
C#
C#一分钟浅谈:Lambda 表达式和匿名方法
本文详细介绍了C#编程中的Lambda表达式与匿名方法,两者均可用于定义无名函数,使代码更简洁易维护。文章通过基础概念讲解和示例对比,展示了各自语法特点,如Lambda表达式的`(parameters) => expression`形式及匿名方法的`delegate(parameters)`结构。并通过实例演示了两者的应用差异,强调了在使用Lambda时应注意闭包问题及其解决策略,推荐优先使用Lambda表达式以增强代码可读性。
30 8
|
2月前
|
图形学 C# 开发者
全面掌握Unity游戏开发核心技术:C#脚本编程从入门到精通——详解生命周期方法、事件处理与面向对象设计,助你打造高效稳定的互动娱乐体验
【8月更文挑战第31天】Unity 是一款强大的游戏开发平台,支持多种编程语言,其中 C# 最为常用。本文介绍 C# 在 Unity 中的应用,涵盖脚本生命周期、常用函数、事件处理及面向对象编程等核心概念。通过具体示例,展示如何编写有效的 C# 脚本,包括 Start、Update 和 LateUpdate 等生命周期方法,以及碰撞检测和类继承等高级技巧,帮助开发者掌握 Unity 脚本编程基础,提升游戏开发效率。
45 0
|
2月前
|
C#
C# async await 异步执行方法
C# async await 异步执行方法
44 0
|
2月前
|
C# 图形学
小功能⭐️C#控制小数点后位数的方法
小功能⭐️C#控制小数点后位数的方法
|
2月前
|
C#
WPF/C#:数据绑定到方法
WPF/C#:数据绑定到方法
34 0
|
4月前
|
C#
技术经验分享:C#DUID的用法及取得整数的几个方法
技术经验分享:C#DUID的用法及取得整数的几个方法
62 1