欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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
来自: