1 属性

这得先从属性开始说,为什么外部代码访问对象内部的数据用属性而不是直接访问呢,这样岂不是更方便一些,但是事实证明直接访问是不安全的。那么,Anders Hejlsberg(安德斯·海尔斯伯格)就为C#加入了属性这种语法糖,用起来跟数据成员一样,但实际上是 setXX()和getXX(),既安全又方便。

属性:是访问对象的首选方式,因为它们禁止外部代码访问对象内部的数据存储机制的实现。

public int MyIntProp
{
get
{
//property get code
}
set
{
//Proerty set code
}
}

1.1 get关键字

get块必须有一个属性的返回值,简单的属性一般与私有字段相关联,以控制对这个字段的访问,此时get块可以直接返回该字段的值,例如:

private int myInt;

public int myIntProp
{
  get
  {
    return myInt;
  }
  set
  {
    //Property set code.
  }
}

类外部的代码不能直接访问这个myInt字段,私有的,必须使用属性来访问该字段。

1.2 set关键字

set函数以类似的方法把一个值赋给字段。这里使用value表示用户提供的属性值:

private int myInt;
public int myIntProp
{
  get
  {
    return myInt;
  }
  set
  {
    myInt = value;
  }
}

value等于类似与属性相同的值,所以如果属性和字段使用相同的类型,就不必担心数据类型转换了。

这个简单的属性只能直接访问myInt字段。在对操作进行更多的控制的时候,属性的真正作用才能发挥出来,例如,使用下面的代码实现set块:

set
{
  if(value >= && value <= )
  myInt = value;
}

只用赋给属性的值在1~10之间,才会改myInt。此时,要做一个重要的设计选择:如果使用了无效值,该怎么办:

  • 什么也不做
  • 给字段赋默认值
  • 继续执行,就好像没有发生错误一样,但记录下来该事件,以备将来分析
  • 抛出异常

一般情况下,后面两个选择效果较好,选择哪个选项取决于如何使用类,以及给用户授予多少控制权。抛出异常给用户提供的控制权相当的大,例如:

set
{
  if(value >= && value <= )
    myInt = value;
  else
    throw (new ArgumentOutOfRangeException("myIntProp",value,"myIntProp must be assigned a value between 0 and 10."))
}

这可以在使用属性的代码中通过try...catch...finaly逻辑来处理。

注:属性可以使用virtual、override和abstract关键字,就像方法一样,但这几个关键字不能用于字段。最后,如上述,访问器可以有自己的访问性。

实例:

public class MyClass
{
public readonly string Name;
private int intVal; public int Val
{
  get
  {
  return intVal;
  }
  set
  {
    if (value >= && value <= )
      intVal = value;
    else
      throw (new ArgumentOutOfRangeException("Val",value,"Val must be assigned a value between 0 ang 10."));
  }
}
public override string ToString()
{
  return "Name:"+Name+"\nVal:"+Val;
}
private MyClass(): this("Default Name")
{ }
public MyClass(string newName)
{
  Name = newName;
  intVal = ;
}
} static void Main(string[] args)
{
  Console.WriteLine("Creating object myobj...");
  MyClass myObj = new MyClass("My Object");
  Console.WriteLine("myObj created.");
  for (int i = -; i <= ; i++ )
  {
    try
    {
      Console.WriteLine("\nAttempting to assign {0} to myObj.val...",i);
      myObj.Val = i;
      Console.WriteLine("Value {0} assigned to myObj.val.", myObj.Val);
    }
    catch(Exception e)
    {
      Console.WriteLine("Exception {0} throw.",e.GetType().FullName);
      Console.WriteLine("Message:\n\"{0}\"",e.Message);
    }
  }
  Console.WriteLine("\nOutputting myObj.ToString()...");
  Console.WriteLine(myObj.ToString());
  Console.WriteLine("myObj.ToString() Output.");
  Console.ReadKey();
}

Main()中的的代码创建并使用在MyClass.cs中定义的MyClass类的实例。实例化这个类必须使用非默认的构造函数来进行,因为MyClass类的默认构造函数是私有的。

Main()试着给myObj(MyClass的实例)的Val属性赋值。for循环在两次中赋值-1和0,try..catch...结构用于检测抛出的异常。把-1赋给属性时,会抛出System.ArgumentOutOfException类型的异常,catch块中的代码会把改异常的信息输出到控制台窗口中。在下一个循环中,值0成功的赋给了Val属性,通过这个属性再把值赋给私有字段intVal。

2 自动属性

但是呢,安德斯还是觉得代码太多,还应该在优化一下,就想出了自动属性。

自动属性。利用自动属性,可以用简化的语法声明属性,C#编译器会自动添加未键入的内容,具体而言,编译器会声明一个用于存储属性的私有字段,并在属性的get和set块中使用该字段(非常贴心),我们无需考虑细节。

public int MyIntProp

{
  get;
  set;
}

我们按照通常的方式定义属性的可访问性、类型和名称。但是没有给get和set块提供实现的代码。这些块的实现代码(和底层的字段)由编译器提供。

使用自动属性时,只能通过属性访问数据,不能通过底层的私有字段来访问,我们不知道底层私有字段的名称(该名称是编译期间定义的)。但这并不是一个真正意义上的限制,因为可以直接使用属性名。自动属性的唯一限制是他们必须包含get和set存储器,无法使用这种方法定义只读和只写属性。

3 对象初始化器

对象初始化器定义:初始化器分为对象初始化器和集合初始化器,此处指我们讲的是对象初始化器,

作用:用较少的代码创建一个新对象并为对象的若干属性和公共数据成员进行赋值。

谈到初始化器,先谈一下构造函数,构造从表面意思就知道这是用来构建类的(当然初始化一些成员也是属于构建的范围,但还有其他作用)

对象初始化过程:先定义类的属性,再实例化和初始化这个类。

定义类的属性用自动属性来定义,实例化和初始化这个类的一个对象实例就必须用C#里面的默认的无参构造函数来实现下段代码。

先看一个类定义:

public class Curry
{
public string MainIngredient { get; set; }
public string Style { get; set; }
public int Spiciness { get; set; }
}

这个类有3 个属性,用自动属性语法来定义。如果希望实例化和初始化这个类的一个对象实例,就必须执行如下几个语句:

  1. 第一种方式
Curry tastyCurry = new Curry();

tastyCurry.MainIngredient = "panir tikka";

tastyCurry.Style = "jalfrezi";

tastyCurry.Spiciness = ;

如果类定义中未包含构造函数,这段代码就使用C#编译器提供的默认无参数构造函数。这种方式还是有点复杂,应该还有比这个简单的,你猜对了。

2.第二种方式(去掉括号)

Curry tastyCurry=new Curry
{
tastyCurry.MainIngredient = "panir tikka", tastyCurry.Style = "jalfrezi", tastyCurry.Spiciness = ,
}

然后问题就来了,如果你有10个数据成员,实例化和初始化这个类的一个对象实例要写多少个?为了简化这个过程,安德斯又机灵了一下想到了更高级的方式,采用一个合适的非默认构造函数。

如下:

Class tastyCurry =new Curry(“panir tikka ”, “jalfrezi”,);

这段代码工作的很好,它会强制使用Curry类的代码使用这个构造函数,这将阻止前面默认使用无参构造函数的代码运行。

4 匿名类型

你以为这样就非常方便了么,只能说你太年轻,天外有天,人外有人,看看我们的题目,对了,就是它,我们的主角,匿名类型。

匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型。 类型名由编译器生成,并且不能在源代码级使用。 每个属性的类型由编译器推断。可通过使用 new 运算符和对象初始值创建匿名类型。

来,我们举个栗子。

以下示例显示了用两个名为 Amount 和 Message 的属性进行初始化的匿名类型。

var v = new { MainIngredient =“panir tikka ”, Style =“jalfrezi” Spiciness=};  

Console.WriteLine(v.MainIngredient + v.Style+v.Spiciness);  

看见了么,对,没错,就是这么简单。

备注:关于C# 的匿名类型为什么要限制属性为只读呢?

来自知乎网友的一段话我觉得说的挺好的。

其实匿名类型是C# 3.0引入的,C# 3.0引入的所有新特性基本都是为了实现LINQ这一伟大的语言特性。匿名类型是为了解决LINQ中选择部分字段以及多字段作为分组依据聚合或是多字段联接的问题的。所以,说白了匿名类型设计的目标就是元组。匿名类型本质上就是关系模型中的元组在C#里面的映射。元组显然是不可变的,匿名类型也没有必要设计成可变的来自找麻烦。

至此,匿名类型的由来就大致讲清楚了,主要是因为工程师要简便优化代码,匠心创作,匿名类型由此诞生。

参考文献:【c#入门经典第五版】【知乎

友情提示

作者:mhq_martin

博客园地址:http://www.cnblogs.com/mhq-martin/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

【C#复习总结】匿名类型由来的更多相关文章

  1. Entity Framework 6 Recipes 2nd Edition(11-5)译 -> 从”模型定义”函数返回一个匿名类型

    11-5. 从”模型定义”函数返回一个匿名类型 问题 想创建一个返回一个匿名类型的”模型定义”函数 解决方案 假设已有游客(Visitor) 预订(reservation)房间(hotel ) 的模型 ...

  2. Linq专题之提高编码效率—— 第二篇 神一样的匿名类型

    说起匿名类型,我们都知道这玩意都是为linq而生,而且匿名类型给我们带来的便利性大家在实战中应该都体会到了,特别适合于一次性使用,临时 使用这些场景,虽然说是匿名类型,也就是说是有类型的,只是匿名了而 ...

  3. 《精通C#》自定义类型转化-扩展方法-匿名类型-指针类型(11.3-11.6)

    1.类型转化在C#中有很多,常用的是int类型转string等,这些都有微软给我们定义好的,我们需要的时候直接调用就是了,这是值类型中的转化,有时候我们还会需要类类型(包括结构struct)的转化,还 ...

  4. 当匿名类型遇上Distinct

    首先定义一个简单类,并重写ToString方法. public class CommidityFilter { public string Property { get; set; } public ...

  5. C#简单问题,不简单的原理:不能局部定义自定义类型(不含匿名类型)

    今天在进行代码测试时发现,尝试在一个方法中定义一个委托,注意是定义一个委托,而不是声明一个委托变量,在编写的时候没有报错,VS也能智能提示,但在编译时却报语法不完整,缺少方括号,但实际查询并没有缺少, ...

  6. JavaScriptSerializer 中的匿名类型 转json

    二:JavaScriptSerializer 中的匿名类型 这个类型我想大家都清楚,不过性能更高的方式应该是用JsonConvert吧,但这个不是本篇讨论的话题,我们重点来看看匿名类型的Json序列化 ...

  7. 使用ExposedObject对Asp.net MVC中匿名类型的JsonResult做单元测试

    返回JsonResult是MVC中的常见返回值类型,而且简单方便的方式是结合匿名类型一起使用. 比如: public ActionResult PreviewEmail() { …… return J ...

  8. 15.C#回顾及匿名类型(八章8.1-8.5)

    今天的篇幅应该会很长,除了回顾前面学的一些,还有写一些关于匿名类型的相关知识,总体上对后续的学习很有帮助,学好了,后面更容易理解,不明白的,那就前面多翻几次,看多了总是会理解的.那么,进入正题吧. 自 ...

  9. 编写高质量代码改善C#程序的157个建议[匿名类型、Lambda、延迟求值和主动求值]

    前言 从.NET3.0开始,C#开始一直支持一个新特性:匿名类型.匿名类型由var.赋值运算符和一个非空初始值(或以new开头的初始化项)组成.匿名类型有如下基本特性: 1.既支持简单类型也支持复杂类 ...

随机推荐

  1. github生成SSH公钥

    ssh-keygen -t rsa -C "your_email@youremail.com" 然后输入github上的密码 Enter passphrase (empty for ...

  2. spring boot 系列之二:spring boot 如何修改默认端口号和contextpath

    上一篇文件我们通过一个实例进行了spring boot 入门,我们发现tomcat端口号和上下文(context path)都是默认的, 如果我们对于这两个值有特殊需要的话,需要自己制定的时候怎么办呢 ...

  3. html标记语言 --表单

    html标记语言 --表单 七.表单 1.表单标记 1.1表单中的内容 <form></form>定义表单的开始位置和结束位置,表单提交时的内容就是<form>表单 ...

  4. Text-鼠标点击事件

    from tkinter import * import webbrowser master=Tk() text=Text(master,width=50,height=20) text.pack() ...

  5. 模拟Paxos算法及其简单学习总结

    一.导读 Paxos算法的流程本身不算很难,但是其推导过程和证明比较难懂.在Paxos Made Simple[1]中虽然也用了尽量简化的流程来解释该算法,但其实还是比较抽象,而且有一些细节问题没有交 ...

  6. 确认过眼神,你是喜欢Stream的人

    摘要:在学习Node的过程中,Stream流是常用的东东,在了解怎么使用它的同时,我们应该要深入了解它的具体实现.今天的主要带大家来写一写可读流的具体实现,就过来,就过来,上码啦! 码前准备 在写代码 ...

  7. [LeetCode] Second Minimum Node In a Binary Tree 二叉树中第二小的结点

    Given a non-empty special binary tree consisting of nodes with the non-negative value, where each no ...

  8. 使用.Net+非关系型数据库MongoDB 实现LBS商家按距离排序_按离我最近排序

    .Net MongoDB LBS地理位置定位 开发过程,实现商家按距离排序 前言: 在使用美团点外卖,看电影,找好吃的时候,经常会注意到软件有一个按距离排序,找离我最近的商家,心中有一些疑问,.Net ...

  9. PHPCMS v9.6.0 wap模块 SQL注入

    调试这个漏洞的时候踩了个坑,影响的版本是php5.4以后. 由于漏洞是由parse_str()函数引起的,但是这个函数在gpc开启的时候(也就是php5.4以下)会对单引号进行过滤\'  . 看这里: ...

  10. python 网路爬虫(二) 爬取淘宝里的手机报价并以价格排序

    今天要写的是之前写过的一个程序,然后把它整理下,巩固下知识点,并对之前的代码进行一些改进. 今天要爬取的是淘宝里的关于手机的报价的信息,并按照自己想要价格来筛选. 要是有什么问题希望大佬能指出我的错误 ...