C#中双问号、双冒号等几个特殊关键字

@:放在字符串的前面,直接让字符串原样输出。常用于:sql语句、路径等

string sql = @"select * from tablename where id = '1'";
string path = @"C:\filename.xml";
如果字符串里边包含双引号时,需要两个双引号代表一个双引号
string s = @"He said,""yes""";
//s输出He said,"yes"

is:检查变量是不是给定的类型,是返回true,不是返回false,特点是不会触发异常

int i = 5;
bool check = i is int;//check = true

as:用于在兼容的引用类型之间执行转换,转换失败则返回 Null,因此在使用前,需进行判空操作。例如

string s = someObject as string;
if (s != null)
{
    // someObject is a string.
}
  • as运算符类似于强制转换操作;但是,如果转换不可行,as会返回null而不是引发异常。更严格地说,这种形式的表达式 等效于
expression is type ? (type)expression : (type) null
  • as 运算符只执行引用转换和装箱转换。as运算符无法执行其他转换,如用户定义的转换,这类转换应使用cast表达式来执行。

sizeof:用于获取数值类型的大小(单位为字节)

int intSize = sizeof(int);//intSize = 4

typeof:返回Type对象,该对象保存类型信息,参数为一个 类

Type myType = typeof(int);
console.writeline("Type:{}",myType);
//输出Type:System.Int32

checked:检测操作的溢出情况

short a =33000,b=320000;
short myShort = checked((short)(a+b));
//error

unchecked:忽略溢出,接受结果而不管溢出情况,默认是不检查溢出的

short a =32000,b=30000;
short myShort = checked((short)(a+b));
//忽略error

Guid:全局唯一标示符,是一个128位的字符串,用在任何要以唯一方式来表示某个事物时。

uniquecode = Guid.NewGuid ();
console.WriteLine("myCode:{}",uniquecode.ToString());
//输出:myCode:cabfe0ba-fa72-4c5c-969f-e76821949ff1

?:可空类型,主要是兼容数据库字段的可空。数据库中所有类型的字段均可以为空

public class student
{
private string name;
private int? age=null;
public string Name
{
get { return name; }
set { name = value; }
}
public int? Age
{
get { return age; }
set { age = value; }
}
}
student s = new student();
s.Age = null;//是允许的

??:null接合操作符,也可以说是双问号操作符,意思是取所赋值??左边的,如果左边为null,取所赋值??右边的

DateTime? createDate = null;
DateTime? defaultDate= null;
DateTime secondDate = DateTime.Now;
createDate = createDate ??defaultDate??secondDate;

// 如果createDate 为空,则对defaultDate求值,如果defaultDate不为空,则将defaultDate赋值给createDate 。否则继续计算secondDate,是不是null都赋值给createDate ,因为是最后一个表达式

:: 作用域操作符

两个冒号表示作用域操作符。::操作符在其左操作数的作用域内找到其右操作数的名字。用于访问某个命名空间中的名字,如std::cout,表明名字cout来自命名空间std。同样的可以用来从某个类取名字,如string::size_type,表明size_type是string类定义的。

这里面::前面是GAC的标示符global,用法比较特殊,和.不是一个类型的东西。

global 是 C# 2.0 中新增的关键字,理论上说,如果代码写得好的话,根本不需要用到它。 

假设你现在写了一个类,名字叫 System。那么当你再在代码里写 System 的时候,编译器就不知道你是要指你写的 System 类还是系统的 System 命名空间,而 System 命名空间已经是根命名空间了,无法再通过完全限名来指定。在以前的 C# 版本中,这就是一个无法解决的问题。现在,可以通过 
global::System
来表示 System 根命名空间,而用你自己的 
MyNamespace.System 
来表示自己的类。

另外,也可以用在命名空间别名上(也可以用 . 点号)在此示例中,命名空间 System 用于包括类 TestClass,因此必须使用 global::System.Console 来引用 System.Console 类,该类被System 命名空间隐藏。 而且,别名 colAlias 用于引用命名空间 System.Collections;因此,将使用此别名而不是命名空间来创建System.Collections.Hashtable 的实例。

using colAlias = System.Collections;
namespace System
{
    class TestClass
    {
        static void Main()
        {
            // Searching the alias:
            colAlias::Hashtable test = new colAlias::Hashtable();

            // Add items to the table.
            test.Add("A", "1");
            test.Add("B", "2");
            test.Add("C", "3");

            foreach (string name in test.Keys)
            {
                // Searching the global namespace:
                global::System.Console.WriteLine(name + " " + test[name]);
            }
        }
    }
}

12、=>

Lambda表达式的运算符是=>,运算符左边列举出了需要的参数,右边定义了赋予Lambda变量的方法的实现代码

List user = new List{ new User{Id=1,Name=“LiSi”,Age=22}, new User{Id=2,Name=“ZhangSan”,Age=25} };
//获取特定人时所用的过滤条件,p参数属于User类型
var results = user.Where(p => p.Name == “LiSi”).ToList(); //用User对象的Age值计算平均年龄
var average = user.Average(p => p.Age);
13、ref

ref 关键字使参数按引用传递,也就是说它能够让你直接对原数进行操作,而不是对那个原数的Copy进行操作。若要使用 ref 参数,则方法定义和调用方法都必须显式使用 ref 关键字,而且传递到 ref 参数的参数必须最先初始化,例如:

class RefExample
{
static void Method(ref int i)
{
i = 44;
}
static void Main()
{
int val = 0;
Method(ref val); // val is now 44
}
}

14、out

out是传出参数,与ref有点像,但偏重于输出,而且不用初始化,通过执行使用out参数的方法逻辑,out后面的数接受并返回这个值,比如你写一个方法返回dataset,同时你还想返回页数,怎么办?方法一般不能返回多个值啊,这个时候out就可以返回多个值,是不是很爽,你需要多个值得时候别忘了out这厮啊

public DataSet getData(out int count)
{
dataset ds=bll.getdata(10,20);
获取第11条到第20条数据,但是不可能只显示共有10条记录吧,那么我们就可以用out了
int rcount=bll.GetCount();//比方说这个是取总记录数的
count=rcount;

return ds; 

}

//显示的时候

public void showdata()
{
int count=0;

gridview1.datasource=getData(out count); 
gridview1.databind(); 

label1.text=“共有”+count.tostring()+“条记录”;
}

15、params

params主要的用处是在给函数传参数的时候用,就是当函数的参数不固定的时候。在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字!

注意事项:

(1)若形参表中含一个参数数组,则该参数数组必须位于形参列表的最后;

(2)参数数组必须是一维数组;

(3)不允许将params修饰符与ref和out修饰符组合起来使用;

(4)与参数数组对应的实参可以是同一类型的数组名,也可以是任意多个与该数组的元素属于同一类型的变量;

(5)若实参是数组则按引用传递,若实参是变量或表达式则按值传递。

(6)用法:可变的方法参数,也称数组型参数,适合于方法的参数个数不知的情况,用于传递大量的数组集合参数;当使用数组参数时,可通过使用params关键字在形参表中指定多种方法参数,并在方法的参数表中指定一个数组,形式为:方法修饰符 返回类型 方法名(params 类型[] 变量名)

如带有参数的SQL 语句,不同的表的字段数量也不同,当你更新修改的时候就可以用params

16、using

这个再也熟悉不过了,常见三种用法

(1)引用命名空间,例如:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
(2)创建别名(类或者命名空间的别名)

using MyControle=System.Console;
class UsingEx
{
public static void Main()
{
MyConsole.WriteLine(“应用了类的别名”);
}
}

(3)自动清理资源

using (SqlConnection conn = new SqlConnection(_connstr))
//这样你就不用手工清理连接资源了

17、this

(1)表示当前实例

(2)索引器关键字

(3)隐藏父类同名方法的关键字

(4)扩展方法的关键字

18、<%= %>主要用于在前台输入后台变量,比如后台中有个public string a = “abc”;前台aspx页面<%=a%>就可以取到后台中a的值:abc

19、<%: %>是在asp.net mvc项目中绑ViewData用的,而且前提是视图引擎用的是aspx的才行

20、__makeref、__reftype、__refvalue、__arglist 这类关键字才叫奇葩

看IL指令到mkrefany, 文档中说它的作用是: “push a typed reference on the stack”, 不知道在C#的何种语法会用上这条指令, 于是Google之, 发现了从来没有看过的C#关键字:

Object obj = new Object();

TypedReference typedref = __makeref(obj);

Type type = __reftype(typedref);

Object sameObj = __refvalue( typedref,Object);

对应的IL是:

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 33 (0x21)
.maxstack 1
.locals init ([0] object obj,
[1] typedref ‘typedref’,
[2] class [mscorlib]System.Type ‘type’,
[3] object sameObj)
IL_0000: nop
IL_0001: newobj instance void [mscorlib]System.Object::.ctor()
IL_0006: stloc.0
IL_0007: ldloca.s obj
IL_0009: mkrefany [mscorlib]System.Object
IL_000e: stloc.1
IL_000f: ldloc.1
IL_0010: refanytype
IL_0012: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0017: stloc.2
IL_0018: ldloc.1
IL_0019: refanyval [mscorlib]System.Object
IL_001e: ldind.ref
IL_001f: stloc.3
IL_0020: ret
} // end of method Program::Main

可以发现:

TypedReference对象在IL中是typedref类型;

IL_0009使用mkrefany生成了一个类型为Object的typedref;

IL_0010使用refanytype从typedref中得到了一个RuntimeTypeHandle, 随即调用一个方法得到Type对象;

IL_0019使用refanyval从typedref中获取了一个类型为Object的引用, 从后面一句ldind.ref可以知道refanyval压栈的是一个managed pointer(&类型), 而不是普通的reference, ldind.ref把栈顶的managed pointer转换成了普通的reference.

这三个操作对应的也可以直接用TypedReference的静态方法实现:

MyObj obj = new MyObj(99);

TypedReference tr = __makeref(obj); // TypedReference.MakeTypedReference

Type type = Type.GetTypeFromHandle(TypedReference.TargetTypeToken(tr));

MyObj sameObj = (MyObj)TypedReference.ToObject(tr);

在C#的unsafe语境中可以使用&运算符获取一个值类型量/对象的地址, 但不可以获取一个引用类型对象的地址, 因为引用类型字段值的分配完全受运行时控制, 但TypedReference可以看成为任何托管对象的指针, typeref在CLI里存在的理由是给所谓的动态语言提供一种动态的方式来访问对象.

21、__arglist
大家都知道printf是一个不定参数数量的函数, 它的第二个参数是用…声明的, 如果要在C#中使用P/Invoke应用这个函数该如何声明呢? params是.NET中特有的不定参数数量的实现, 但我用

extern static int printf(string format, params object[] args);

声明的printf永远都不能正常工作(谁能?), 还好C#提供了一个__arglist关键字来支持古老的vararg, 如何使用__arglist声明和调用printf见下面的代码:

[DllImport(“msvcrt.dll”)]

extern static int printf(string format, __arglist);

static unsafe void Main(string[] args)

{

printf("%d %.2f %s", __arglist(3, 0.4567, "asdf"));

}

我们甚至可以自己写一个带vararg参数的方法, TypedReference也派上用场了:

static void MyPrint(__arglist)

{

ArgIterator itr = new ArgIterator(__arglist);

while (itr.GetRemainingCount() > 0)

{

    Console.WriteLine(TypedReference.ToObject(itr.GetNextArg()));

}

}

static unsafe void Main(string[] args)

{

MyPrint(__arglist("asdfasdf", 2, 1.2f, 2.4d, 123L, new object()));
发布了737 篇原创文章 · 获赞 74 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/sinolover/article/details/104677966