C#的基础
一:Ref和Out 的区别:
1、使用ref型参数时,传入的参数必须先被初始化。对out而言,必须在方法中对其完成初始化。
2、使用ref和out时,在方法的参数和执行方法时,都要加Ref或Out关键字。以满足匹配。
3、out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候。
4、ref是有进有出,而out是只出不进。
二:装箱和拆箱
在应用中最大的一个意义就在于:理解数据从栈移动到堆的过程中所发生的性能消耗问题,反之亦然。
考虑一下以下的代码片段,当我们将一个值类型转换为引用类型,数据将会从栈移动到堆中。相反,当我们将一个引用类型转换为值类型时,数据也会从堆移动到栈中。
不管是在从栈移动到堆还是从堆中移动到栈上都会不可避免地对系统性能产生一些影响。
于是,两个新名词横空出世:当数据从值类型转换为引用类型的过程被称为“装箱”,而从引用类型转换为值类型的过程则被成为“拆箱”。
如果你编译一下上面这段代码并且在ILDASM(一个IL的反编译工具)中对其进行查看,你会发现在IL代码中,装箱和拆箱是什么样子的。下图则展示了示例代码被编译后所产生的IL代码。
装箱和拆箱的性能问题
为了弄明白到底装箱和拆箱会带来怎样的性能影响,我们分别循环运行10000次下图所示的两个函数方法。其中第一个方法中有装箱操作,另一个则没有。我们使用一个Stopwatch对象来监视时间的消耗。
具有装箱操作的方法花费了3542毫秒来执行完成,而没有装箱操作的方法只花费了2477毫秒,整整相差了1秒多。而且,这个值也会因为循环次 数的增加而增加。也就是说,我们要尽量避免装箱和拆箱操作。在一个项目中,如果你需要装箱和装箱,请仔细考虑它是否是绝对必不可少的操作,如果不是,那么 尽量不用。
虽然以上代码段没有展示拆箱操作,但其效果同样适用于拆箱。你可以通过写代码来实现拆箱,并且通过Stopwatch来测试其时间消耗。
三:值类型和引用类型
值类型将数据和内存都保存在同一位置,而一个引用类型则会有一个指向实际内存区域的指针。
通过下图,我们可以看到一个名为i的整形数据类型,它的值被赋值到另一个名为j的整形数据类型。他们的值都被存储到了栈上。
当我们将一个int类型的值赋值到另一个int类型的值时,它实际上是创建了一个完全不同的副本。换句话说,如果你改变了其中某一个的值,另一个不会发生改变。于是,这些种类的数据类型被称为“值类型”。
当我们创建一个对象并且将此对象赋值给另外一个对象时,他们彼此都指向了如下图代码段所示的内存中同一块区域。因此,当我们将obj赋值给 obj1时,他们都指向了堆中的同一块区域。换句话说,如果此时我们改变了其中任何一个,另一个都会受到影响,这也说明了他们为何被称为“引用类型”。
哪些是值类型,哪些是引用类型?
在.NET中,变量是存储到栈还是堆中完全取决于其所属的数据类型。比如:‘String’或‘Object’属于引用类型,而其他.NET基元数据类型则会被分配到栈上。下图则详细地展示了在.NET预置类型中,哪些是值类型,哪些又是引用类型。
堆(Stack)
栈(Heap)
堆和栈是.Net中内存分配的两种方式。
为了理解栈和堆,让我们通过以下的代码来了解背后到底发生了什么。
public void Method1()
{
// Line 1
int i=4;
// Line 2
int y=2;
//Line 3
class1 cls1 = new class1();
}
代码只有三行,现在我们可以一行一行地来了解到底内部是怎么来执行的。
Line 1:当这一行被执行后,编译器会在栈上分配一小块内存。栈会在负责跟踪你的应用程序中是否有运行内存需要
Line 2:现在将会执行第二步。正如栈的名字一样,它会将此处的一小块内存分配叠加在刚刚第一步的内存分配的顶部。你可以认为栈就是一个一个叠加起来的房间或盒子。在栈中,数据的分配和解除都会通过LIFO (Last In First Out)即先进后出的逻辑规则进行。换句话说,也就是最先进入栈中的数据项有可能最后才会出栈。
Line 3:在第三行中,我们创建了一个对象。当这一行被执行后,.NET会在栈中创建一个指针,而实际的对象将会存储到一个叫做“堆”的内存区域中。“堆”不会监测运行内存,它只是能够被随时访问到的一堆对象而已。不同于栈,堆用于动态内存的分配。
这
里需要注意的另一个重要的点是对象的引用指针是分配在栈上的。 例如:声明语句 Class1 cls1;
其实并没有为Class1的实例分配内存,它只是在栈上为变量cls1创建了一个引用指针(并且将其默认职位null)。只有当其遇到new关键字时,它
才会在堆上为对象分配内存。
离开这个Method1方法时(the fun):现在执行控制语句开始离开方法体,这时所有在栈上为变量所分配的内存空间都会被清除。换句话说,在上面的示例中所有与int类型相关的变量将会按照“LIFO”后进先出的方式从栈中一个一个地出栈。
需要注意的是:这时它并不会释放堆中的内存块,堆中的内存块将会由垃圾回收器稍候进行清理。
现在我们许多的开发者朋友一定很好奇为什么会有两种不同类型的存储?我们为什么不能将所有的内存块分配只到一种类型的存储上?
如果你观察足够仔细,基元数据类型并不复杂,他们仅仅保存像 ‘int i =
0’这样的值。对象数据类型就复杂了,他们引用其他对象或其他基元数据类型。换句话说,他们保存其他多个值的引用并且这些值必须一一地存储在内存中。对象
类型需要的是动态内存而基元类型需要静态内存。如果需求是动态内存的话,那么它将会在堆上为其分配内存,相反,则会在栈上为其分配。
五: 获取程序当前路径
有很多方法能够获取程序当前路径
- 获取当前 Thread 的当前应用程序域的基目录,它由程序集冲突解决程序用来探测程序集。(推荐使用这种方式)
string str = System.AppDomain.CurrentDomain.BaseDirectory;
返回结果:
X:\xxx\xxx\ (.exe文件所在的目录+”\”)
- 获取新的 Process 组件并将其与当前活动的进程关联的主模块的完整路径,包含文件名(进程名)。
string str = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
返回结果:
X:\xxx\xxx\xxx.exe (.exe文件所在的目录+.exe文件名)
- 获取和设置当前目录(即该进程从中启动的目录)的完全限定路径。
string str = System.Environment.CurrentDirectory;
返回结果:
X:\xxx\xxx (.exe文件所在的目录)
- 获取和设置包含该应用程序的目录的名称。
string str = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
返回结果:
X:\xxx\xxx\ (.exe文件所在的目录+”\”)
- 获取应用程序的当前工作目录(不可靠)。
string str = System.IO.Directory.GetCurrentDirectory();
返回结果:
X:\xxx\xxx (.exe文件所在的目录)
- 获取启动了应用程序的可执行文件的路径,不包括可执行文件的名称。(仅限WinForm程序)
string str = System.Windows.Forms.Application.StartupPath;
返回结果:
X:\xxx\xxx (.exe文件所在的目录)
- 获取启动了应用程序的可执行文件的路径,包括可执行文件的名称。(仅限WinForm程序)
string str = System.Windows.Forms.Application.ExecutablePath;
返回结果:
X:\xxx\xxx\xxx.exe (.exe文件所在的目录+.exe文件名)
- 获取包含清单的已加载文件的路径或 UNC 位置。
public static string sApplicationPath = Assembly.GetExecutingAssembly ( ).Location;
返回结果:
X:\xxx\xxx\xxx.dll (.dll文件所在的目录+.dll文件名)
- 获取当前进程的完整路径,包含文件名(进程名)。
string str = this.GetType ( ).Assembly.Location;
返回结果:
X:\xxx\xxx\xxx.exe (.exe文件所在的目录+.exe文件名)
六:读写程序配置文件
程序配置文件作为程序重要配置信息文件,在项目开发过程中,我们可能需要对其进行读写操作。
配置文件在不同类型的项目中,名称不同:
- Web项目配置文件为:web.config
- WinForm项目配置文件为:app.config
.Net Framework为我们提供了相应类进行程序配置文件操作。
在配置文件中,有几个节点是可以进行自定的:
- appSettings:程序设置,key-value键值对信息
- connectionStrings:连接字符串
appSettins读写操作
配置文件内容格式:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DemoKey" value="*" />
</appSettings>
</configuration>
读:
String str = ConfigurationManager.AppSettings["DemoKey"];
写:
Configuration cfa = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
cfa.AppSettings.Settings.Add("key", "Name")||cfa.AppSettings.Settings["key"].Value = "name";
cfa.Save();
注意:调用ConfiguionManager必须要先在工程里添加system.configuration.dll程序集的引用。
connectionStrings读写操作
获取ConnectionStrings配置节中的值
//// <summary>
/// 获取ConnectionStrings配置节中的值
/// </summary>
/// <returns></returns>
public static string GetConnectionStringsElementValue()
{
ConnectionStringSettings settings = System.Configuration.ConfigurationManager.ConnectionStrings["connectionString"];
return settings.ConnectionString;
}
保存值
//// <summary>
/// 保存节点中ConnectionStrings的子节点配置项的值
/// </summary>
/// <param name="elementValue"></param>
public static void ConnectionStringsSave(string ConnectionStringsName, string elementValue)
{
System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.ConnectionStrings.ConnectionStrings["connectionString"].ConnectionString = elementValue; config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("connectionStrings");
}
八:常用的Stream和Byte[]操作
Stream和Byte[]是C#中比较底层的操作,直接针对文件内容进行操作,所以掌握常用的操作,能增强我们开发技能。
转换操作
Stream 和 byte[] 之间的转换
将 Stream 转成 byte[]
public byte[] StreamToBytes(Stream stream)
{
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
// 设置当前流的位置为流的开始
stream.Seek(0, SeekOrigin.Begin);
return bytes;
}
将 byte[] 转成 Stream
public Stream BytesToStream(byte[] bytes)
{
Stream stream = new MemoryStream(bytes);
return stream;
}
byte[]与string的转换代码
将string转换为byte[]
System.Text.UnicodeEncoding converter = new System.Text.UnicodeEncoding();
byte[] inputBytes =converter.GetBytes(inputString);
string inputString = converter.GetString(inputBytes);
将byte[]转换为string
string inputString = System.Convert.ToBase64String(inputBytes);
byte[] inputBytes = System.Convert.FromBase64String(inputString);
FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
Stream 和 文件之间的转换
将 Stream 写入文件
public void StreamToFile(Stream stream,string fileName)
{
// 把 Stream 转换成 byte[]
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
// 设置当前流的位置为流的开始
stream.Seek(0, SeekOrigin.Begin);
// 把 byte[] 写入文件
FileStream fs = new FileStream(fileName, FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(bytes);
bw.Close();
fs.Close();
}
从文件读取 Stream
public Stream FileToStream(string fileName)
{
// 打开文件
FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
// 读取文件的 byte[]
byte[] bytes = new byte[fileStream.Length];
fileStream.Read(bytes, 0, bytes.Length);
fileStream.Close();
// 把 byte[] 转换成 Stream
Stream stream = new MemoryStream(bytes);
return stream;
}
图片和byte[]互相转换
二进制转换成图片
MemoryStream ms = new MemoryStream(bytes);
ms.Position = 0;
Image img = Image.FromStream(ms);
ms.Close();
this.pictureBox1.Image
图片转二进制
Bitmap BitReturn = new Bitmap();
byte[] bReturn = null;
MemoryStream ms = new MemoryStream();
BitReturn.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
bReturn = ms.GetBuffer();
C#的基础的更多相关文章
- java基础集合经典训练题
第一题:要求产生10个随机的字符串,每一个字符串互相不重复,每一个字符串中组成的字符(a-zA-Z0-9)也不相同,每个字符串长度为10; 分析:*1.看到这个题目,或许你脑海中会想到很多方法,比如判 ...
- node-webkit 环境搭建与基础demo
首先去github上面下载(地址),具体更具自己的系统,我的是windows,这里只给出windows的做法 下载windows x64版本 下载之后解压,得到以下东西 为了方便,我们直接在这个目录中 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- Golang, 以17个简短代码片段,切底弄懂 channel 基础
(原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...
- [C#] C# 基础回顾 - 匿名方法
C# 基础回顾 - 匿名方法 目录 简介 匿名方法的参数使用范围 委托示例 简介 在 C# 2.0 之前的版本中,我们创建委托的唯一形式 -- 命名方法. 而 C# 2.0 -- 引进了匿名方法,在 ...
- HTTPS 互联网世界的安全基础
近一年公司在努力推进全站的 HTTPS 化,作为负责应用系统的我们,在配合这个趋势的过程中,顺便也就想去搞清楚 HTTP 后面的这个 S 到底是个什么含义?有什么作用?带来了哪些影响?毕竟以前也就只是 ...
- Swift与C#的基础语法比较
背景: 这两天不小心看了一下Swift的基础语法,感觉既然看了,还是写一下笔记,留个痕迹~ 总体而言,感觉Swift是一种前后端多种语言混合的产物~~~ 做为一名.NET阵营人士,少少多多总喜欢通过对 ...
- .NetCore MVC中的路由(1)路由配置基础
.NetCore MVC中的路由(1)路由配置基础 0x00 路由在MVC中起到的作用 前段时间一直忙于别的事情,终于搞定了继续学习.NetCore.这次学习的主题是MVC中的路由.路由是所有MVC框 ...
- .NET基础拾遗(5)多线程开发基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...
- .NET 基础 一步步 一幕幕[面向对象之方法、方法的重载、方法的重写、方法的递归]
方法.方法的重载.方法的重写.方法的递归 方法: 将一堆代码进行重用的一种机制. 语法: [访问修饰符] 返回类型 <方法名>(参数列表){ 方法主体: } 返回值类型:如果不需要写返回值 ...
随机推荐
- CentOS 使用 Xfce 桌面并通过 xrdp 登录
基础环境 CentOS 7.1 最小化安装 安装步骤 以下步骤,均通过ssh连接到主机进行操作. 安装桌面支持 首先安装桌面支持 yum groupinstall "Server with ...
- 【1】AQS详解
概述: 它内部实现主要是状态变量state和一个FIFO队列来完成,同步队列的头结点是当前获取到同步状态的结点,获取同步状态state失败的线程,会被构造成一个结点加入到同步队列尾部(采用自旋CAS来 ...
- Element link doesn't have required attribute property
前端标准http://validator.w3.org/ 拒绝你的代码时报 Element link doesn't have required attribute property 把样式链接 &l ...
- DNS缓存欺骗攻击
1.受影响的无线网络设备 一些D-link.TP-Link.Belkin.Linksys及IPTime等品牌无线路由器存在安全隐患. 2.漏洞描述 攻击者向DNS服务器注入非法网络域名地址,如果服务器 ...
- 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域
- js scroll函数
$(function () { $(".sticky").hide(); var top = $(window).scrollTop(); if (top >= 100) { ...
- C# Regex正则常用方法的使用
using System; using System.Collections; using System.Configuration; using System.Data; using System. ...
- Postfix 邮件服务 - postfix服务
postfix 邮件服务 也是基于sendmail (需要关闭或卸载sendmail)postfix 是一个电子邮件服务器,它为了改良sendmail邮件服务器而产生的,并且它的配置文件比sendma ...
- Elastic Job入门(2) - 使用
运维平台 elastic-job-lite-console-${version}.tar.gz可通过mvn install编译获取,下载源码,进入console目录,执行: mvn clean ins ...
- sqlalchemy-查询
User这个类创建的表 User1这个类创建的表 基本查询结果 # 1 查看sql原生语句 rs =session.query(User).filter(User.username=='budon ...