.NET学习笔记(三) ------系统类型和通用操作 (下)

C# ADO.NET 操作数据库 学习笔记 / Execute执行命令,两种情况:a.返回影响行数(Insert, Update, Delete语句返回的都是行数)。// 必须把SqlParameter和SqlCommand建立联系,sql字符中的参数才能被sql参数的值替换。// 让dt自动加载dataReader中的数据(把dataReader的数据转存到dt中)// SqlDbType是sql参数在数据库中的数据类型。// Size是sql参数在数据库中的数据类型长度。// 执行命令,把命令返回的结果集存储到。// 在sql中使用参数。 阅读详情

四 装箱与拆箱


前面主要都讨论的是同类型直接的转换,引用到引用,值类型到值类型。还有一种用的非常多的就是引用类型和值类型之间的转换,比如传递参数时,值类型存储到一个引用类型时。也就是经常可以听到的装箱和拆箱操作。这确实是个非常复杂的地方。因为引用类型和值类型的内存空间是不同的,也就导致了许多性能问题和拷贝使用等问题。对于每一种值类型,运行库都提供一种相应的已装箱类型,这是与值类型有着相同状态和行为的类。当需要已装箱的类型时,某些语言要求使用特殊的语法(如C++要用关键字);而另外一些语言会自动使用已装箱的类型(C#是自动的)。在定义值类型时,需要同时定义已装箱和未装箱的类型。

由前面我们可以知道,值类型是分配在线程的堆栈上,而引用类型是分配在托管堆上的。所谓的‘装箱’也就是把值类型转化为一个引用类型的过程。实际上也就是把分配在堆栈上的对象,装箱(可能说打包比较形象,前面说过分配在托管堆的对象回又个附加成员),然后重新分配到托管堆上的。装箱操作通常由以下几步组成:
1:在托管堆上为新生成的引用类型分配内存空间。这个空间大小为值类型本身大小和附加成员(方法表指针和SyncBlockIndex)
2:将值类型实例的字段拷贝到托管堆上新分配对象的内存中。
3:返回托管堆中新分配对象的地址。这个地址就是一个指向对象的引用。值类型实例也就变成了一个引用类型对象。 

//声名一个值类型
struct Point
{
  
public Int32 x,y;
}



class app
{
 
static void Main()
 
{
   ArrayList a 
= new ArraryList();
   Point p ;            
//值类型分配到线程堆栈上
   for(Int32 i = 0; i < 10 ;i++)
   
{
      p.x 
= p.y = 1;    //初始化值类型成员
      a.Add(p);         //Add(Object obj)方法要接受一个引用类型,所以会对p进行装箱操作。
   }

 }

}

上面的代码中,会先在托管堆上为point分配一个内存空间,然后把p中字段传到新的内存空间中去。然后返回这个新内存空间的地址,也就是指向这个新分配的对象的地址给add方法。这时传个add方法的就是托管堆上新对象了。这个装箱过程对于C#来说是自动完成的。这个新的对象会最后会被垃圾回收器回收掉。而这个时候,之前的分配在线程堆栈上的对象p则可以被重用或者释放。经过装箱后的值类型的生存期超过了未装箱的值类型。

但我们想从ArrayList中取得这个新的Point对象时,因为他是值类型,我们就必须把他从托管堆上拷贝到线程堆栈上进行使用。这就涉及到拆箱的操作。拆箱操作是指获取已装箱值类型的对象的地址的过程。这也就是说拆箱和装箱不是想对的。往往在拆箱后还要把此对象的字段拷贝到线程堆栈上的值类型实例中。可见拆箱的代价很下,他只获得对象的地址。要注意的是,在拆箱后进行转型时,必须先转为他原来未装箱时的类型。

Int32 x = 5;
Object o 
= x ;
Int16 y 
=(Int16)o;    //错误
Int16 y =(Int32)(Int16)o; //正确

在装箱和拆箱的过程中,会从速度和内存两方面损伤应用程序的性能。所以我们应该清楚编译器何时产生这些操作的指令,并在程序中尽可能的减少这种情况。看下面的代码,为了修改P,两次装箱代价非常大。

Point p;
p.x 
= p.y = 1;
Object o 
= p;    //p第一次装箱

p 
= (Point) o;   //拆箱并拷贝
p.x = 2;     
o 
= p;           //第二次装箱,又会分配一个新的内存空间。

下面看一些列子:

using System;

class ex1
{
    
public static void Main()
    
{
        Int32 v 
= 5 ;
        Object o 
= v;   
        v 
= 123;
        Console.WriteLine (v 
+ "," + (Int32)o);

    }



}

这样的程序进行了几次装箱操作呢?答案是3次。可以通过看IL代码来了解:

.method public hidebysig static void  Main() cil managed
{
  
// 代码大小       46 (0x2e)
  .maxstack  3
  .locals init (int32 V_0,
           
object V_1)
  
//把5入栈
  IL_0000:  ldc.i4.5
  IL_0001:  stloc.
0
  
//装箱
  IL_0002:  ldloc.0
  IL_0003:  box        [mscorlib]System.Int32    
  IL_0008:  stloc.
1

  
//把123存到v中
  IL_0009:  ldc.i4.s   123
  IL_000b:  stloc.
0
  
//对V装箱
  IL_000c:  ldloc.0
  IL_000d:  box        [mscorlib]System.Int32  

  
//把,入栈 
  IL_0012:  ldstr      ","
  
//对o拆箱
  IL_0017:  ldloc.1
  IL_0018:  unbox      [mscorlib]System.Int32
  IL_001d:  ldind.i4

  
//对o装箱,可以看到调用了String::Concat方法都接受Object型
  IL_001e:  box        [mscorlib]System.Int32    
  IL_0023:  call       
string [mscorlib]System.String::Concat(object,
                                                              
object,
                                                              
object)
  IL_0028:  call       
void [mscorlib]System.Console::WriteLine(string)
  IL_002d:  ret
}
 // end of method ex1::Main

第一次装箱和你好看出,而后两次是发生在Console.WriteLine方法中的,对于连接多个字符串时,他接受的类型是Object型,看到上面的方法中第一个参数v和最后一个(Int32)o,这两个地方发生了装箱。

对于这个Console.WriteLine方法可以改为Console.WriteLine (v + "," + o),这样的话,最后一个参数o已经是引用参数了,就会减少一次装箱。重新编译后查看IL代码会发现 代码大小变小了。

查阅MSDN可以发现Console.WriteLine方法有多种重载的方法,比如为Int32,Char,Single等基元类型提供了重载,这样在单独输出这些类型的时候就避免的装箱的操作。其他一些类型也有类似重载方法,目的就是为了减少值类型装箱操作,提高效率。我们自己在写程序时,如果一个类型可能导致系统对他反复装箱,我们就应该自己为他装箱

Int32 v = 5 ;
Console.WriteLine (
"{0},{1},{2}",v,v,v); //3次装箱

Int32 v 
= 5 ;
Object o 
= v;    //1次装箱    
Console.WriteLine ("{0},{1},{2}",o,o,o); 

 

五 继承和重写通用操作


前面提到过Object类型有一些公有的方法,而所有的类型都继承与Object类型,所以他们都能继承这些通用操作方法。当然Object中实现的这些方法可能不能满足我们的类型的需求,这个时候我们可以对他进行重写,来实现自己的方法。

比如Equals方法,在Object中实现是用来判断2个引用类型是否指向同一个对象。这对于我们要判断2个对象的值相等时就显得不够了,所以我们需要对他进行重写。对于重写了Equals方法的类型,一般也要求重写他的GetHashCode方法。这里Equlas方法的重写比较麻烦,要分为引用类型和值类型的重写。而引用类型中又分为基类重写了此方法和没有重写此方法两钟。对Equals方法重新写主要是比较:
1:对象是否为空
2:调用基类此方法判断(对于引用类型的基类重写了此方法的需要判断,如果调用基类会导致调用Object.Equlas是就不调用)
3:2个对象类型是否相同
4:2个对象中的所有字段是否相等
5:最后需要对 ==,!=,这些操作符进行重载

对于值类型来说,因为所有值类型是继承ValueType,而他又是继承Object的。ValueType重写了Equlas方法。他在内部使用了反射,然后比较每个字段。这样效率比较低,但所有的值类型都能继承。

另外一个常用的方法就是MemberwiseClone。他是创建一个新的类型实例,并将去字段设置为和this对象的字段相同。最后返回创建实例引用。在系统中拷贝一个对象会经常用到,其中拷贝有两种,浅拷贝和深拷贝。利用MemberwiseClone来进行的是深拷贝,因为他创建一个新的对象,然后把他的字段和被拷贝的对象置成相同的。而浅拷贝,是指当对象的字段值被拷贝时,字段引用的对象不会拷贝。也就是说只拷贝堆栈上的数据,而不拷贝所指向托管堆栈的对象。

而一个对象是否允许被Clone就要看他是否实现了ICloneable接口。

Public interface ICloneable
{ 
  Object Clone();
}


//定义一个类型
Class MyClass
{
  ArrayList 
set;
  MyClass();
}


//浅拷贝
MyClass o1 = new MyClass();
MyClass o2 
= o1; 

//要想实现深拷贝,在Clone方法中调用MemberwiseClone就行了
Class MyClass : ICloneable
{
 
public Object Clone()
 
{
   
return MemberwiseClone();
 }

}

MyClass o1 
= new MyClass();
MyClass o2 
= o1.Clone();

 

//另一种方法就是自己分配一个新对象,实现深拷贝,不调用MemberwiseClone。
Class MyClass : ICloneable
{
 ArrayList 
set;

 
//定义Clone方法的私有构造器
 private MyClass(ArrayList set)
 
{
   
this.set = (ArrayList)set.Clone()
 }


 
public Object Clone()
 
{
   
return new MyClass(set);  //构造一个新对象,构造器参数为原来对象中使用的ArrayList。
 }

}


MyClass o1 
= new MyClass();
MyClass o2 
= o1.Clone();

上面讲的都是引用类型,而对于值类型他本身就支持浅拷贝。如果我们自己定义的一个值类型,也希望实现深拷贝,就需要我们自己来实现ICloneable接口,最好不要使用MemberwiseClone方法,而是分配一个对象,自己实现深拷贝。

 这里是关于MemberwiseClone方法:

using System;
using System.Collections;

class ex :ICloneable
{
    
internal int x;
    
internal ex1 y;
    
public ex()
    
{
        x 
= 0;
        y 
= new ex1();
    }


    
public Object Clone()
    
{
        
return MemberwiseClone();
    }

}


class ex1
{
    
internal int n;
    
public ex1()
    
{
        n 
= 0;
    }


    
public ex1(int n)
    
{
        
this.n = n;
    }

}


class examlpe
{
    
static void Main()
    
{
        ex e1 
= new ex();
        ex e2 
= (ex)e1.Clone();
        e1.x 
= 1;
        e1.y.n 
= 5;
        Console.WriteLine(e1.x);
        Console.WriteLine(e1.y.n);
        Console.WriteLine(e2.x);
        Console.WriteLine(e2.y.n);
    }

}


输出结果入下:

1
5
0
5

从结果可以看出,实例的int字段指向不同地址,修改了e1.x不会修改e2.x。而对于引用字段y,在Clone时,只复制了y的引用,ex1.y和ex2.y指向同一个地址。所以修改了一个会影响另一个。

MemberwiseClone是浅拷贝。对于浅拷贝是把值拷到新空间中,而深拷贝把引用拷到新的空间中。而和是否创建新的实例没有关系。因为以前一直受下面这个影响,所以对浅拷贝理解的比较浅。

ex e1 = new ex();
ex e2 
= e1;


一直以为只有这一种浅拷贝方式,2个对象指向同一个实例就是签拷贝。但MemberwiseClone方法可以看出。创建了新的实例,2个对象指向不同的实例,也可能是浅拷贝。因为实例中如果是引用字段,复制的时候只是复制的引用地址。所以看到底是深拷贝还是浅拷贝,还是要看实现的时候对引用对象是复制地址还是值。而MemberwiseClone是复制引用地址,所以是浅拷贝。

 

六 命名空间和程序集


这个在书中出现了就顺便提下。很多人可能弄不清楚命名空间和程序集的关系。用一句比较通俗的说法就是,程序集是类型的物理存储形式,他表示某个类型在哪个文件中。而命名空间则是类型的逻辑存储形式。就好比学校里的学生就是类型,他们住的寝室好比程序集,而他们的班级是命名空间。不同程序集的类型可能属于同一个命名空间。一个程序集中的类型也可能包含属于多个命名空间。

.NET 8 通用权限框架 前后端分离,开箱即用 基于.NET 8(Furion)/SqlSugar实现的通用管理平台。整合最新技术,模块插件式开发,前后端分离,开箱即用。让开发更简单、更通用、更流行! 阅读详情

相关推荐

.NET面试经典三问:什么是.NET?什么是.NET Framework?什么是.NET Core?

本文将从上往下,循序渐进的介绍一系列相关.NET的概念,先从类型系统开始讲起,我将通过跨语言操作这个例子来逐渐引入一系列.NET的相关概念,这主要包括:CLS、CTS(CLI)、FCL、Windows下CLR的相关核心组成、Windows下托管程序运行概念、什么是.NET Framework,.NET Core,.NET Standard及一些VS编译器相关杂项和相关阅读链接。完整的从上读到下则你可以理解个大概的.NET体系。

B1ueSocks的博客 647

内存 IL过程 装箱 拆箱

int i = 4;2 object o = i;3 object o2 = o;4 Console.WriteLine(ReferenceEquals(o, o2)); //true5 o = 8;6 Console.WriteLine(ReferenceEquals(o, o2)); //false7 8 Console.WriteLine("i={0

个人笔记 242

使用MySQL和.NET进行通用的CRUD操作

在本文中,我们介绍了如何使用MySQL和.NET框架进行通用的CRUD操作。我们首先建立了与MySQL数据库的连接,然后演示了如何执行创建、读取、更新和删除操作的示例代码。MySQL是一种流行的关系型数据库管理系统,而.NET是一个强大的开发框架,用于构建各种类型的应用程序。在本文中,我们将探讨如何使用MySQL和.NET框架进行通用的CRUD(创建、读取、更新和删除)操作。首先,我们需要在.NET应用程序中建立与MySQL数据库的连接。为此,我们可以使用.NET提供的MySQL连接对象。

CodeBandit的博客 198

.NET通用开发框架

在开源中国社区,简单整理了下比较好的.NET通用开发框架。一个好的通用框架大概包括:开源、扩展性好、灵活性好、复用性好、维护性好、易测试、易发布、易部署、快速业务搭建(或业务集成)、通用性强、参考资料多、持续技术支持、社区疑难问题建设。.NET通用开发框架 :0.ASP.NET MVCASP.NET MVC,在现有的ASP.NET 3.5运行时的基础上提供了一个新的MVC框架。开发人员可以用MVC...

weixin_34150830的博客 649

.NET C# 通用SQL操作类的构建与应用

本文还有配套的精品资源,点击获取 简介:在.NET框架中,通过使用C#语言创建一个通用SQL操作类,可以简化数据库CRUD操作和事务处理。该类封装了SqlConnection、SqlCommand等对象的基本使用,支持参数化查询和事务处理,旨在提高开发效率和数据库交互的安全性。本教程详细讲解了如何实现此类,并提供了实际应用示例。 1. C#与数据库操作概述 ...

weixin_34547628的博客 1825

ASP.NET开发者必备的通用类库

ASP.NET是微软公司推出的一个用于开发动态网页、网络应用和Web服务的开发框架。自2002年首次发布以来,ASP.NET经历了多个版本的迭代,逐步发展成为一个成熟的Web开发解决方案。通过引入.NET框架,ASP.NET不仅为开发人员提供了丰富的开发工具和语言支持,还大幅提升了Web应用的安全性、性能和可维护性。DB.cs作为.NET平台下通用的数据库操作类,它致力于简化数据库的连接、查询、更新等操作流程。其设计理念基于以下几个核心点:抽象性。

weixin_42186387的博客 824

Admin.NET:有可能是.NET最好用的通用权限管理平台开发框架

Admin.NET,一个基于.NET 8(Furion)/SqlSugar的通用管理平台,引领了权限开发框架的新潮流。通过模块化的插件式开发,以及前后端分离的设计,它提供了一个开箱即用的解决方案,集成了多租户、缓存、数据校验、鉴权、事件总线、动态API、通讯、远程请求、任务调度、gRPC等众多黑科技。本文将深入探讨Admin.NET的技术特性,以及其在.NET开发领域的潜力和影响。

chenchuang0128的博客 7753

新手必看:如何用T265和ROS实现无人机室内精准定位(附PX4参数配置)

本文详细介绍了如何使用Intel Realsense T265和ROS实现无人机室内精准定位,包括硬件准备、ROS数据流架构设计、PX4参数配置及系统验证。特别针对视觉SLAM技术在室内环境中的应用,提供了从零搭建到进阶优化的全流程解析,适合无人机开发新手快速上手。

weixin_30642561的博客 405

.NET框架程序设计读书笔记(三)--.net框架类库(FCL)和通用类型系统、代码互操作

function StorePage(){d=document;t=d.selection?(d.selection.type!=None?d.selection.createRange().text:):(d.getSelection?d.getSelection():);void(keyit=window.open(http://www.365key.com/storeit.as

390

Spark数据倾斜问题+解决方案

1、数据倾斜 数据倾斜指的是,并行处理的数据集中,某一部分(如Spark或Kafka的一个Partition)的数据显著多于 其它部分,从而使得该部分的处理速度成为整个数据集处理的瓶颈 数据倾斜俩大直接致命后果 1)数据倾斜直接会导致一种情况:Out Of Memory 2)运行速度慢 主要是发生在Shuffle阶段。同样Key的数据条数太多了。导致了某个key(下图中的80亿条)所在的Task数 据量太大了。远远超过其他Task所处理的数据量 一个经验结论是:一般情况下,OOM的原因都是数据倾斜 2、如

诸葛搏毅 6771

COLMAP生成MVSNet数据集

COLMAP生成MVSNet数据集

qq_43307074的博客 5637

C#/VB.NET 获取电脑属性(硬盘ID、硬盘容量、Cpu序列号、MAC地址、系统类型)

我们可以运行DOS命令,或者查看注册表,查看硬盘的序列号。如图中所示:都是HGST HTS725050A7E630。 大部分情况下,大家都可以通过如下获取: Dim key As RegistryKey = Registry.LocalMachine Dim subKey = key.OpenSubKey("HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0")

陈表达的博客 1733

SpringBoot与锐浪报表服务器深度整合实战指南

本文详细介绍了SpringBoot与锐浪报表服务器的深度整合实战指南,包括环境准备、Windows/Linux系统配置、核心功能实现代码解析及常见问题解决方案。通过具体案例和代码示例,帮助开发者高效完成报表系统的集成与部署,提升企业级应用的报表处理能力。

weixin_29238147的博客 370

Asp.Net 操作 Sqlserver通用类

<br />基于SqlServer的数据通用类<br />using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.SqlClient; using System.Configuration; namespace DAL { /// <summary> /// 说明:数据访问通用类,

人生若只如初见....... 3731

代谢组数据分析(四):多种差异分析方法筛选代谢物标记物

本文详细介绍了代谢组学中常用的差异分析方法,包括倍数变化法、T检验、VIP值在OPLS-DA中的应用。通过这些方法,研究者能够筛选出在不同组别间具有显著差异的代谢物,为疾病诊断和治疗提供线索。文章还提供了一个整合的R函数run_metabolomeDA,便于研究人员进行一站式差异分析,并展示了如何生成火山图来可视化结果。

专注生信领域 6773

虚幻商城 蓝图汇总

虚幻引擎是一个非常强大和灵活的游戏开发工具,拥有众多的蓝图可以帮助我们更高效地工作。

一碗清深的博客 1508

ADO.NET基础_使用ADO.NET访问和操作数据库

ADO.NET基础_使用ADO.NET访问和操作数据库 作者:田超凡 版权所有,转载请注明原作者,仿冒侵权必究法律...

田宝宝的博客 1万+

YOLOv8交通管理 详细解释及代码完整示例

YOLOv8在交通管理中的应用及代码实现 摘要:本文详细介绍了YOLOv8在交通管理领域的应用场景,包括车辆检测、违规行为识别和行人安全预警等。文章提供了完整的Python代码示例,展示了如何使用YOLOv8进行交通对象检测和车辆计数统计。代码包含模型加载、对象检测、结果可视化等功能模块,并给出了性能优化和功能扩展建议。该实现基于Ultralytics框架,支持多种YOLOv8模型选择,可根据实际需求调整模型大小和检测参数。

u010117029的博客 153

ASP.NET Core 中的 .NET 通用主机详解

ASP.NET Core 中的 .NET 通用主机详解

寒冰屋的专栏 237

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【ASP.NET Core】ASP.NET Core 中的 .NET 通用主机详解

通用主机由 Microsoft.Extensions.Hosting 提供,核心职责包括:• 应用生命周期管理(启动、运行、停止)• 内建依赖注入(DI)容器支持• 配置系统(支持 JSON、环境变量、命令行等)• 内置日志记录支持• 支持后台服务(如 IHostedService)不仅服务于 ASP.NET Core,还适用于控制台程序、Windows 服务、Worker Service 等。

德仔 335

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

创建基于.NET的通用数据库访问层

一.引言: ADO.NET为应用程序开发人员提供了一种全新的数据库访问机制,它使得数据库编程变得相当容易。然而,在运用ADO.NET进行数据库编程时,开发人员往往会因为不注意某些细节问题而使得应用程序的可扩展性很差,也即某个数据库应用程序只能应用于某个特定类型的数据库,而不能和更多的其他类型的数据库进行交互或是移植到其它数据库平台下。本文将深入研究如何运用ADO.NET中的接口技术来实现通用数

夏风鱼的专栏 1223

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

上一篇: .NET学习笔记(二) ------系统类型和通用操作 (上)
下一篇: .NET学习笔记(四) ------类型设计(上)
cc_net
博客等级 码龄21年 901粉丝 54原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值