【C#复习总结】匿名类型由来

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 >= 0 && value <= 10)
  myInt = value;
}

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

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

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

set
{
  if(value >= 0 && value <= 10)
    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 >= 0 && value <= 10 )
      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 = 0;
}
}

static void Main(string[] args)
{
  Console.WriteLine("Creating object myobj...");
  MyClass myObj = new MyClass("My Object");
  Console.WriteLine("myObj created.");
  for (int i = -1; i <= 0; 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 = 8;

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

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

Curry tastyCurry=new Curry
{
    tastyCurry.MainIngredient = "panir tikka",

    tastyCurry.Style = "jalfrezi",

    tastyCurry.Spiciness = 8,
}

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

如下:

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

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

4 匿名类型

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

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

来,我们举个栗子。

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

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

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/

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

原文地址:https://www.cnblogs.com/mhq-martin/p/8973686.html

时间: 2024-10-10 13:58:51

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

定义类+类实例化+属性+构造函数+匿名类型var+堆与栈+GC回收机制+值类型与引用类型

为了让编程更加清晰,把程序中的功能进行模块化划分,每个模块提供特定的功能,而且每个模块都是孤立的,这种模块化编程提供了非常大的多样性,大大增加了重用代码的机会. 面向对象编程也叫做OOP编程 简单来说面向对象编程就是结构化编程,对程序中的变量结构划分,让编程更清晰. 类的概念: 类实际上是创建对象的模板,每个对象都包含数据,并提供了处理和访问数据的方法. 类定义了类的每个对象(称为实例)可以包含什么数据和功能. 类中的数据和函数称为类的成员:数据成员        函数成员 数据成员: 数据成员

编写高质量代码改善C#程序的157个建议——建议26:使用匿名类型存储LINQ查询结果

建议26:使用匿名类型存储LINQ查询结果 从.NET3.0开始,C#开始支持一个新特性:匿名类型.匿名类型有var.赋值运算符和一个非空初始值(或以new开头的初始化项)组成.匿名类型有如下基本特性: 即支持简单类型也指出复杂类型.简单类型必须是一个非空初始值,复杂类型则是一个以new开头的初始化项. 匿名类型的属性是只读的,没有属性设置器,它一旦被初始化就不可更改. 如果两个匿名类型的属性值相同,那么就认为这两个匿名类型相等. 匿名类型可以再循环中用作初始化器. 匿名类型支持智能感知. 匿名

C#超级实用的一种类型—匿名类型

顾名思义 匿名类型就是没有名字的类型.当一个新的匿名对象定义与前面已经存在的类型定义的内部变量类型相同时,编译器就会只生成一个类定义,而不是各一个.匿名类型对象中仍然可以再包含匿名对象. 在C#3.0中允许我们在程序中声明一个临时的类型来存储数据,例如: class Program { static void Main(string[] args) { //声明一个匿名对象,拥有 Name和Age 属性 var obj = new { Name = "Joey", Age = 25 }

如何把匿名类型.GetType()返回的对象传进泛型里面[转]

//怎么取得匿名类型的Type放到 //泛型T当中?? var 匿名 = new { A = 0, B = 1 }; Type t = 匿名.GetType(); //然后下面 var xx = dbContext.Database.SqlQuery<t>("sql"); //就悲剧了 var xx2 = dbContext.Database.SqlQuery<dynamic>("sql"); //xx2有列表,但是都是Object..~~~

C# 匿名类型

c#3.0引入匿名类型,其由编译器动态生成而非显式定义. using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks; namespace Project5_33{    class Program    {        static void Main(string[] args)        {            var p

理解隐式类型、对象初始化程序和匿名类型

在C# 3.0中,几乎每个新特性都是为LINQ服务的.所以,本文将介绍下面几个在C# 3.0中引入的新特性: 自动实现的属性 隐式类型的局部变量 对象和集合初始化程序 隐式类型的数组 匿名类型 其实这几个特性都是比较容易理解的,对于这几个特性,编译器帮我们做了更多的事情(想想匿名方法和迭代器块),从而简化我们的代码. 自动实现的属性 在C# 3.0以前,当我们定义属性的时候,一般使用下面的代码 public class Book { private int _id; private string

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

今天的篇幅应该会很长,除了回顾前面学的一些,还有写一些关于匿名类型的相关知识,总体上对后续的学习很有帮助,学好了,后面更容易理解,不明白的,那就前面多翻几次,看多了总是会理解的.那么,进入正题吧. 自动实现属性 我们的很多工作都是由编译器帮我们去完成,如我们要说的自动实现属性.使用自动实现属性时,C#3执行了一个简单的编译转换,在类的内部生成一个私有的字段,使用不友好的命名(防止命名冲突).在C#2中允许为取值和赋值方法指定不同的访问权限,现在我们还可以创建静态的自动属性. 隐式类型 使用隐式类

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

11-5. 从”模型定义”函数返回一个匿名类型 问题 想创建一个返回一个匿名类型的”模型定义”函数 解决方案 假设已有游客(Visitor) 预订(reservation)房间(hotel ) 的模型,如Figure 11-5所示. Figure 11-5. A model for hotel reservations 想要返回每位游客房间预订条数和带来的总收入.因为很多地方需要这些信息,所以想要创建一个”模型定义”函数,接受一个查询参数,返回一个包含游客合计信息的匿名类型的集合: 2. 把Li

匿名类型(C# 编程指南)

匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型. 类型名由编译器生成,并且不能在源代码级使用. 每个属性的类型由编译器推断.可通过使用 new 关键字和对象初始值创建匿名类型. 以下示例显示了用两个名为Amount和Message的属性进行初始化的匿名类型. 1 var v = new { Amount = 108, Message = "Hello" }; 2 3 // Rest the mouse pointer over v.Amo