C#基础概念二十五问 21-25
程序员文章站
2022-06-14 13:38:31
21.p/invoke是什么? 答: 在受控代码与非受控代码进行交互时会产生一个事务(transition) ,这通常发生在使用平台调用服务(platform&n...
21.p/invoke是什么?
答:
在受控代码与非受控代码进行交互时会产生一个事务(transition) ,这通常发生在使用平台调用服务(platform invocation services),即p/invoke
如调用系统的 api 或与 com 对象打交道,通过 system.runtime.interopservices 命名空间
虽然使用 interop 非常方便,但据估计每次调用事务都要执行 10 到 40 条指令,算起来开销也不少,所以我们要尽量少调用事务
如果非用不可,建议本着一次调用执行多个动作,而不是多次调用每次只执行少量动作的原则
22.stringbuilder 和 string 的区别?
答:
string 在进行运算时(如赋值、拼接等)会产生一个新的实例,而 stringbuilder 则不会。所以在大量字符串拼接或频繁对某一字符串进行操作时最好使用 stringbuilder,不要使用 string
另外,对于 string 我们不得不多说几句:
1.它是引用类型,在堆上分配内存
2.运算时会产生一个新的实例
3.string 对象一旦生成不可改变(immutable)
3.定义相等运算符(== 和 !=)是为了比较 string 对象(而不是引用)的值
示例:
using system;
using system.collections.generic;
using system.text;
namespace example22
{
class program
{
static void main(string[] args)
{
const int cycle = 10000;
long vtickcount = environment.tickcount;
string str = null;
for (int i = 0; i < cycle; i++)
str += i.tostring();
console.writeline("string: {0} msel", environment.tickcount - vtickcount);
vtickcount = environment.tickcount;
//看到这个变量名我就生气,奇怪为什么大家都使它呢? :)
stringbuilder sb = new stringbuilder();
for (int i = 0; i < cycle; i++)
sb.append(i);
console.writeline("stringbuilder: {0} msel", environment.tickcount - vtickcount);
string tmpstr1 = "a";
string tmpstr2 = tmpstr1;
console.writeline(tmpstr1);
console.writeline(tmpstr2);
//注意后面的输出结果,tmpstr1的值改变并未影响到tmpstr2的值
tmpstr1 = "b";
console.writeline(tmpstr1);
console.writeline(tmpstr2);
console.readline();
}
}
}
结果:
string: 375 msel
stringbuilder: 16 msel
a
a
a
23.explicit 和 implicit 的含义?
答:
explicit 和 implicit 属于转换运算符,如用这两者可以让我们自定义的类型支持相互交换
explicti 表示显式转换,如从 a -> b 必须进行强制类型转换(b = (b)a)
implicit 表示隐式转换,如从 b -> a 只需直接赋值(a = b)
隐式转换可以让我们的代码看上去更漂亮、更简洁易懂,所以最好多使用 implicit 运算符。不过!如果对象本身在转换时会损失一些信息(如精度),那么我们只能使用 explicit 运算符,以便在编译期就能警告客户调用端
示例:
using system;
using system.collections.generic;
using system.text;
namespace example23
{
class program
{
//本例灵感来源于大话西游经典台词“神仙?妖怪?”--主要是我实在想不出什么好例子了
class immortal
{
public string name;
public immortal(string name)
{
name = name;
}
public static implicit operator monster(immortal value)
{
return new monster(value.name + ":神仙变妖怪?偷偷下凡即可。。。");
}
}
class monster
{
public string name;
public monster(string name)
{
name = name;
}
public static explicit operator immortal(monster value)
{
return new immortal(value.name + ":妖怪想当神仙?再去修炼五百年!");
}
}
static void main(string[] args)
{
immortal tmpimmortal = new immortal("紫霞仙子");
//隐式转换
monster tmpobj1 = tmpimmortal;
console.writeline(tmpobj1.name);
monster tmpmonster = new monster("孙悟空");
//显式转换
immortal tmpobj2 = (immortal)tmpmonster;
console.writeline(tmpobj2.name);
console.readline();
}
}
}
结果:
紫霞仙子:神仙变妖怪?偷偷下凡即可。。。
孙悟空:妖怪想当神仙?再去修炼五百年!
24.params 有什么用?
答:
params 关键字在方法成员的参数列表中使用,为该方法提供了参数个数可变的能力
它在只能出现一次并且不能在其后再有参数定义,之前可以
示例:
using system;
using system.collections.generic;
using system.text;
namespace consoleapplication1
{
class app
{
//第一个参数必须是整型,但后面的参数个数是可变的。
//而且由于定的是object数组,所有的数据类型都可以做为参数传入
public static void useparams(int id, params object[] list)
{
console.writeline(id);
for (int i = 0; i < list.length; i++)
{
console.writeline(list[i]);
}
}
static void main()
{
//可变参数部分传入了三个参数,都是字符串类型
useparams(1, "a", "b", "c");
//可变参数部分传入了四个参数,分别为字符串、整数、浮点数和双精度浮点数数组
useparams(2, "d", 100, 33.33, new double[] { 1.1, 2.2 });
console.readline();
}
}
}
结果:
1
a
c
2
d
100
33.33
system.double[]
25.什么是反射?
答:
反射,reflection,通过它我们可以在运行时获得各种信息,如程序集、模块、类型、字段、属性、方法和事件
通过对类型动态实例化后,还可以对其执行操作
简单来说就是用string可以在runtime为所欲为的东西,实际上就是一个.net framework内建的万能工厂
一般用于插件式框架程序和设计模式的实现,当然反射是一种手段可以充分发挥其能量来完成你想做的任何事情(前面好象见过一位高人用反射调用一个官方类库中未说明的函数。。。)
示例:
using system;
using system.collections.generic;
using system.text;
namespace example25lib
{
public class class1
{
private string name;
private int age;
//如果显式的声明了无参数构造函数,客户端只需要用程序集的createinstance即可实例化该类
//在此特意不实现,以便在客户调用端体现构造函数的反射实现
//public class1()
//{
//}
public class1(string name, int age)
{
name = name;
age = age;
}
public void changename(string newname)
{
name = newname;
}
public void changeage(int newage)
{
age = newage;
}
public override string tostring()
{
return string.format("name: {0}, age: {1}", name, age);
}
}
}
反射实例化对象并调用其方法,属性和事件的反射调用略去
using system;
using system.collections.generic;
using system.text;
//注意添加该反射的命名空间
using system.reflection;
namespace example25
{
class program
{
static void main(string[] args)
{
//加载程序集
assembly tmpass = assembly.loadfile(appdomain.currentdomain.basedirectory + "example25lib.dll");
//遍历程序集内所有的类型,并实例化
type[] tmptypes = tmpass.gettypes();
foreach (type tmptype in tmptypes)
{
//获取第一个类型的构造函数信息
constructorinfo[] tmpconsinfos = tmptype.getconstructors();
foreach (constructorinfo tmpconsinfo in tmpconsinfos)
{
//为构造函数生成调用的参数集合
parameterinfo[] tmpparaminfos = tmpconsinfo.getparameters();
object[] tmpparams = new object[tmpparaminfos.length];
for (int i = 0; i < tmpparaminfos.length; i++)
{
tmpparams[i] = tmpass.createinstance(tmpparaminfos[i].parametertype.fullname);
if (tmpparaminfos[i].parametertype.fullname == "system.string")
{
tmpparams[i] = "clark";
}
}
//实例化对象
object tmpobj = tmpconsinfo.invoke(tmpparams);
console.writeline(tmpobj);
//获取所有方法并执行
foreach (methodinfo tmpmethod in tmptype.getmethods())
{
//为方法的调用创建参数集合
tmpparaminfos = tmpmethod.getparameters();
tmpparams = new object[tmpparaminfos.length];
for (int i = 0; i < tmpparaminfos.length; i++)
{
tmpparams[i] = tmpass.createinstance(tmpparaminfos[i].parametertype.fullname);
if (tmpparaminfos[i].parametertype.fullname == "system.string")
{
tmpparams[i] = "clark zheng";
}
if (tmpparaminfos[i].parametertype.fullname == "system.int32")
{
tmpparams[i] = 27;
}
}
tmpmethod.invoke(tmpobj, tmpparams);
}
//调用完方法后再次打印对象,比较结果
console.writeline(tmpobj);
}
}
console.readline();
}
}
}
结果:
name: clark, age: 0
name: clark zheng, age: 27
来自:
答:
在受控代码与非受控代码进行交互时会产生一个事务(transition) ,这通常发生在使用平台调用服务(platform invocation services),即p/invoke
如调用系统的 api 或与 com 对象打交道,通过 system.runtime.interopservices 命名空间
虽然使用 interop 非常方便,但据估计每次调用事务都要执行 10 到 40 条指令,算起来开销也不少,所以我们要尽量少调用事务
如果非用不可,建议本着一次调用执行多个动作,而不是多次调用每次只执行少量动作的原则
22.stringbuilder 和 string 的区别?
答:
string 在进行运算时(如赋值、拼接等)会产生一个新的实例,而 stringbuilder 则不会。所以在大量字符串拼接或频繁对某一字符串进行操作时最好使用 stringbuilder,不要使用 string
另外,对于 string 我们不得不多说几句:
1.它是引用类型,在堆上分配内存
2.运算时会产生一个新的实例
3.string 对象一旦生成不可改变(immutable)
3.定义相等运算符(== 和 !=)是为了比较 string 对象(而不是引用)的值
示例:
using system;
using system.collections.generic;
using system.text;
namespace example22
{
class program
{
static void main(string[] args)
{
const int cycle = 10000;
long vtickcount = environment.tickcount;
string str = null;
for (int i = 0; i < cycle; i++)
str += i.tostring();
console.writeline("string: {0} msel", environment.tickcount - vtickcount);
vtickcount = environment.tickcount;
//看到这个变量名我就生气,奇怪为什么大家都使它呢? :)
stringbuilder sb = new stringbuilder();
for (int i = 0; i < cycle; i++)
sb.append(i);
console.writeline("stringbuilder: {0} msel", environment.tickcount - vtickcount);
string tmpstr1 = "a";
string tmpstr2 = tmpstr1;
console.writeline(tmpstr1);
console.writeline(tmpstr2);
//注意后面的输出结果,tmpstr1的值改变并未影响到tmpstr2的值
tmpstr1 = "b";
console.writeline(tmpstr1);
console.writeline(tmpstr2);
console.readline();
}
}
}
结果:
string: 375 msel
stringbuilder: 16 msel
a
a
a
23.explicit 和 implicit 的含义?
答:
explicit 和 implicit 属于转换运算符,如用这两者可以让我们自定义的类型支持相互交换
explicti 表示显式转换,如从 a -> b 必须进行强制类型转换(b = (b)a)
implicit 表示隐式转换,如从 b -> a 只需直接赋值(a = b)
隐式转换可以让我们的代码看上去更漂亮、更简洁易懂,所以最好多使用 implicit 运算符。不过!如果对象本身在转换时会损失一些信息(如精度),那么我们只能使用 explicit 运算符,以便在编译期就能警告客户调用端
示例:
using system;
using system.collections.generic;
using system.text;
namespace example23
{
class program
{
//本例灵感来源于大话西游经典台词“神仙?妖怪?”--主要是我实在想不出什么好例子了
class immortal
{
public string name;
public immortal(string name)
{
name = name;
}
public static implicit operator monster(immortal value)
{
return new monster(value.name + ":神仙变妖怪?偷偷下凡即可。。。");
}
}
class monster
{
public string name;
public monster(string name)
{
name = name;
}
public static explicit operator immortal(monster value)
{
return new immortal(value.name + ":妖怪想当神仙?再去修炼五百年!");
}
}
static void main(string[] args)
{
immortal tmpimmortal = new immortal("紫霞仙子");
//隐式转换
monster tmpobj1 = tmpimmortal;
console.writeline(tmpobj1.name);
monster tmpmonster = new monster("孙悟空");
//显式转换
immortal tmpobj2 = (immortal)tmpmonster;
console.writeline(tmpobj2.name);
console.readline();
}
}
}
结果:
紫霞仙子:神仙变妖怪?偷偷下凡即可。。。
孙悟空:妖怪想当神仙?再去修炼五百年!
24.params 有什么用?
答:
params 关键字在方法成员的参数列表中使用,为该方法提供了参数个数可变的能力
它在只能出现一次并且不能在其后再有参数定义,之前可以
示例:
using system;
using system.collections.generic;
using system.text;
namespace consoleapplication1
{
class app
{
//第一个参数必须是整型,但后面的参数个数是可变的。
//而且由于定的是object数组,所有的数据类型都可以做为参数传入
public static void useparams(int id, params object[] list)
{
console.writeline(id);
for (int i = 0; i < list.length; i++)
{
console.writeline(list[i]);
}
}
static void main()
{
//可变参数部分传入了三个参数,都是字符串类型
useparams(1, "a", "b", "c");
//可变参数部分传入了四个参数,分别为字符串、整数、浮点数和双精度浮点数数组
useparams(2, "d", 100, 33.33, new double[] { 1.1, 2.2 });
console.readline();
}
}
}
结果:
1
a
c
2
d
100
33.33
system.double[]
25.什么是反射?
答:
反射,reflection,通过它我们可以在运行时获得各种信息,如程序集、模块、类型、字段、属性、方法和事件
通过对类型动态实例化后,还可以对其执行操作
简单来说就是用string可以在runtime为所欲为的东西,实际上就是一个.net framework内建的万能工厂
一般用于插件式框架程序和设计模式的实现,当然反射是一种手段可以充分发挥其能量来完成你想做的任何事情(前面好象见过一位高人用反射调用一个官方类库中未说明的函数。。。)
示例:
using system;
using system.collections.generic;
using system.text;
namespace example25lib
{
public class class1
{
private string name;
private int age;
//如果显式的声明了无参数构造函数,客户端只需要用程序集的createinstance即可实例化该类
//在此特意不实现,以便在客户调用端体现构造函数的反射实现
//public class1()
//{
//}
public class1(string name, int age)
{
name = name;
age = age;
}
public void changename(string newname)
{
name = newname;
}
public void changeage(int newage)
{
age = newage;
}
public override string tostring()
{
return string.format("name: {0}, age: {1}", name, age);
}
}
}
反射实例化对象并调用其方法,属性和事件的反射调用略去
using system;
using system.collections.generic;
using system.text;
//注意添加该反射的命名空间
using system.reflection;
namespace example25
{
class program
{
static void main(string[] args)
{
//加载程序集
assembly tmpass = assembly.loadfile(appdomain.currentdomain.basedirectory + "example25lib.dll");
//遍历程序集内所有的类型,并实例化
type[] tmptypes = tmpass.gettypes();
foreach (type tmptype in tmptypes)
{
//获取第一个类型的构造函数信息
constructorinfo[] tmpconsinfos = tmptype.getconstructors();
foreach (constructorinfo tmpconsinfo in tmpconsinfos)
{
//为构造函数生成调用的参数集合
parameterinfo[] tmpparaminfos = tmpconsinfo.getparameters();
object[] tmpparams = new object[tmpparaminfos.length];
for (int i = 0; i < tmpparaminfos.length; i++)
{
tmpparams[i] = tmpass.createinstance(tmpparaminfos[i].parametertype.fullname);
if (tmpparaminfos[i].parametertype.fullname == "system.string")
{
tmpparams[i] = "clark";
}
}
//实例化对象
object tmpobj = tmpconsinfo.invoke(tmpparams);
console.writeline(tmpobj);
//获取所有方法并执行
foreach (methodinfo tmpmethod in tmptype.getmethods())
{
//为方法的调用创建参数集合
tmpparaminfos = tmpmethod.getparameters();
tmpparams = new object[tmpparaminfos.length];
for (int i = 0; i < tmpparaminfos.length; i++)
{
tmpparams[i] = tmpass.createinstance(tmpparaminfos[i].parametertype.fullname);
if (tmpparaminfos[i].parametertype.fullname == "system.string")
{
tmpparams[i] = "clark zheng";
}
if (tmpparaminfos[i].parametertype.fullname == "system.int32")
{
tmpparams[i] = 27;
}
}
tmpmethod.invoke(tmpobj, tmpparams);
}
//调用完方法后再次打印对象,比较结果
console.writeline(tmpobj);
}
}
console.readline();
}
}
}
结果:
name: clark, age: 0
name: clark zheng, age: 27
来自:
上一篇: 九宫格
下一篇: C#中接口(interface)的理解