想对于【java/c++】这些语言来说,c#的property是比较独特的。给 使用者一种介于方法和字段之间的感觉【对于编译器而言,其实还是一个字段+get/set两个方法】
属性【property】,使用起来还是比较简单的:
public sealed class PropertyDemo{
public string Name{get;set;}
...
}
个人比较喜欢这种方式,另外常见的也有:
public sealed class PropertyDemo{
private string name;
public string Name{
get{
return name;
}
set{
name = value;
}
}
...
}
其实,个人觉得不是很有必要,如果用了属性再写一个私有字段,这和直接去写get/set有区别吗?以最开始那种方式书写方便如果有需要也可以再加进去,使用端代码也不需要有什么变化。最开始那种方式中,使用了C#提供的AIP【自动完成属性】功能,大多数时候这样做就够了,但是,如果对于值有某些要求,比如Age属性,不希望输入值不合法,就可以这样做:
private int age;
public int Age
{
get
{
return age;
}
set
{
if (Age < 0)
{
throw new ArgumentException("Age should greater than 0");
}
age = value;
}
}
当自己实现了set体时,就必须也实现get体。【要么都不实现,要么都实现】
也可以对get/set进行访问控制【只要比属性本身的访问控制严格就行】
总体上说来,使用上是比较简单的,用法上不多说。需要注意的是:
在使用AIP时,编译器会产生一个字段,和两个get/set方法。因此,如果对某个类型要进行序列化/反序列化时,应当注意,由于每次编译,编译器可能产生的字段名称不同,很有可能导致,反序列化失败。
ps: 大多数时候我们很难将字段给予public权限【不符合OO封装】,但是经常性的需要一些get/set方法进行辅助访问【想想java语言一些dto对象】,这确实看起来很不“优雅”,而属性却可以“完美”的解决【编译器把丑陋的地方自己做了】。
对象和初始化器
Student s = new Student(){Name = "xiaolu",Age = "23"};
匿名类型
匿名类型在linq中用的比较多。
var o = new {Name = "xiaolu",Age = "23"};
用到了隐式类型var,因为匿名类型编译器会产生一个类型,这个类型编程者是无法直接获取的。匿名类型无法用返回和参数传递【即使参数类型为object,所有匿名类型也是Object的子类,但是传递进来后,,如何转型?】无法成为返回值也是同样道理,返回类型是不可知的。不过也是可以变通下使用:
public dynamic func(){
return new {Name="xiaolu",Age=23};
}
//Main
//在这里,var或者dynamic关键字都是可以的
dynamic d = func();
//d.Name = "xiaozhang"
//需要注意的是,匿名类型是只读的,上面这句赋值,会造成运行时崩溃。
System.Console.WriteLine(d.Name + " " + d.Age);
//此处即使使用var接收返回值,也是没有智能提示的。
说到匿名类型只读,顺带提下元组类型【System.Tuple】,元组的所有属性也是只读的。
给一个元组的例子,可以体会下用法:
public static Tuple<int, int> MinMax(int a, int b)
{
return new Tuple<int, int>(System.Math.Min(a, b), System.Math.Max(a, b));
}
//Main
var mm = Demo.MinMax(5, 2);
System.Console.WriteLine("min is {0} ,max is {1}", mm.Item1, mm.Item2);
需要注意的是:元组的数据是以ItemN【N是参数数目】,如果对于元组的输入超过8个,可以使用元组的Create函数再次创建一个元组。
public static Tuple<int, int,int,int,int,int,int,Tuple<int,int>> show(int a, int b,int c,int d ,int f,int g,int h,int k,int j)
{
return new Tuple<int, int, int, int, int, int, int, Tuple<int, int>>(a, b, c, d, f, g, h, Tuple.Create(k, j));
}
//Main
var t = Demo.show(1, 2, 3, 4, 5, 6, 7, 8, 9);
System.Console.WriteLine(t.Item1 +"" + t.Item2 + t.Item3 + t.Item4 + t.Item5 + t.Item6 + t.Item7 + t.Rest.Item1 + t.Rest.Item2);//输出 123456789
写完这个,,确实灰常痛苦。不过这里就是把用法记录下来,万一某天某个家伙不嫌麻烦这样写了呢?- -
补充一点,Tuple也可以使用泛型:
public static Tuple<T, T> tuple<T>(T a, T b)
{
return new Tuple<T, T>(a, b);
}
//Main
var t2 = Demo.tuple("string", "s");
System.Console.WriteLine(t2.Item1 + " " + t2.Item2);
//一看就懂,不做过多解释
痛苦的都写了,,再看一个相对有意思的东西:
System.Dynamic.ExpandoObject类,说它有意思是因为,它和dynamic结合起来可以让c#表现的像python这样的动态语言一样,看段代码:
dynamic d = new ExpandoObject();
d.Name = "xiaolu";
d.Age = 23;
d.sex = "男";
//....可以继续任意添加
System.Console.WriteLine("Name:{0},Age:{1},Sex={2}", d.Name, d.Age, d.sex);
有点小酷炫 ,美中不足的是,在输出时,依旧是没有智能提示成员的,想想dynamic语义,也只能算了。
上面说了好多,都是关于无参属性【property】相关的东西,最后为了完整性,说说有参属性【也就是c#中的索引器】,简单的可以看做是对[]操作符的重载。
class IndexDemo
{
private const int max_num = 20;
//简单起见,给个固定值
private string[] ss = new string[max_num];
private int n = 0;//记录个数
public string this[int index]
{
get
{
if (index < 0 || index >= max_num)
{
throw new ArgumentOutOfRangeException();
}
return ss[index];
}
set
{
if (index < 0 || index >= max_num)
{
throw new ArgumentOutOfRangeException();
}
ss[index] = value;
n++;
}
}
}
//Main
IndexDemo id = new IndexDemo();
id[0] = "hello";
id[2] = "world";
id[1] = id[2];
System.Console.WriteLine(id[0] +" " + id[1] + id[2]);
用法相对简单,看看例子。