C# – 特性(基本类型)

值类型和引用类型

值类型:值类型被创建的位置取决于声明的位置:如果声明在托管堆中,他就创建在托管堆中。栈上同理。值类型隐式从System.ValueType派生。所有的值类型都有一个默认的不带参数的构造函数,所以对于引用约束:new(), 值类型是可以匹配的。

引用类型:引用类型的变量(在线程栈)本身是一个引用,引用托管堆中的对象。例如:

class Person{
 void func1(){...}
}
...
static void Main(){
Person p1 = new Person();
Person p2 = p1;
}

p1和p2变量中都在线程栈上。他们都指向托管堆中的Person实例。而Person实例中包含同步索引、类型对象指针以及类型对象指针。类型对象指针指向的是Person的类型对象。因为Person类型对象本身也是System.Type的一个实例(这个实例包含了类型对象指针(System.Type本身也是对象), 同步索引块,静态字段,func1的JIT编译的代码),所以可以使用System.Object中的非虚方法GetType获取这个实例的地址,就可以动态判断任何对象的类型了(RTTI)。

值类型和引用类型的区别

类别 值类型 引用类型
内存分配 一般在线程栈中分配,不需要提领指针,不受垃圾回收控制,不需要类型对象指针和同步索引块 引用类型必须分配上述的三个默认的成员;对象的其他成员总设置为0;托管堆可能执行一次垃圾回收
用作返回值 需要复制到调用者的内存中 传递托管堆中的对象指针
OOP 不能派生,也不能从其他类派生, 从Object直接或者间接派生
装箱与拆箱 自动装/拆箱,例如将值类型转换为Object就是装箱而拆箱只需要获取托管堆中的未装箱的部分的指针(如果包含已装箱的类型引用为null,会抛出NullReference异常;如果拆箱的类型与原始的值类型不一致,抛出InvalidCastException)。C#不允许修改已装箱值类型的字段 已经默认装箱
调用继承的虚方法 CLR将会非虚调用该方法。但是如果重写的虚方法调用了基类的实现,那么会进行装箱以便获得this 虚调用callvirt
调用非虚、继承的方法 进行装箱(例如 MemberwiseCloneGetType 非虚调用 call
接口的方法 通过接口调用,进行装箱 非虚调用 call
接口的方法 通过接口调用接口的方法需要装箱 虚调用
可空Nullable 仅用于值类型 不适用
  • 值类型调用了基类的虚方法,会进行装箱:

C# 代码:

struct MyPoint
    {
        public override String ToString()
        {
            //comment1,return base.ToString();
            return "MyPoint";
        }
    }
    class ValueTypeAndClass
    {
        public static void Main()
        {
            MyPoint my = new MyPoint();
            my.ToString();
        }
    }

没有使用基类的ToString实现,生成的中间语言:

.method public hidebysig virtual instance string 
        ToString() cil managed
{
  ...
  IL_0000:  nop
  IL_0001:  ldstr      "MyPoint"
  ...
} // end of method MyPoint::ToString

解除注释comment1

.method public hidebysig virtual instance string 
        ToString() cil managed
{
  ...
  Part3.Advanced_CSharp_Programming_Structure.MyPoint
  IL_0007:  box        Part3.Advanced_CSharp_Programming_Structure.MyPoint
  IL_000c:  call       instance string [mscorlib]System.ValueType::ToString()
  ...
} // end of method MyPoint::ToString
  • 调用非虚、继承的方法:值类型会装箱
IL_0018:  box        Part3.Advanced_CSharp_Programming_Structure.MyPoint
IL_001d:  call       instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
  • 通过接口调用方法
struct MyPoint:IComparable
    {
        Int32 m_data;
        public Int32 CompareTo(Object obj)
        {
            return m_data.CompareTo(obj);
        }
    ....
    }
    ...
    public static void Main()
    {
            MyPoint mypoint = new MyPoint();
            MyCar mycar = new MyCar();
            ((IComparable)mypoint).CompareTo(50);
            ((IComparable)mycar).CompareTo(50);
    }

值类型装箱:

  IL_0010:  box //将mypoint装箱        Part3.Advanced_CSharp_Programming_Structure.MyPoint
  IL_0015:  ldc.i4.s   50
  IL_0017:  box        [mscorlib]System.Int32//参数装箱
  IL_001c:  callvirt   instance int32 [mscorlib]System.IComparable::CompareTo(object)

System.Object 的部分方法

  • Equals(虚):自反性、对称性、传递性。进行值判断。值类型的直接基类 ValueType ,内部使用了反射比较,所以速度较慢。重写 Equals 需要做如下的事情:IEquatable:类型安全的比较版本;== 和 != ,一般用于比较是否是同一个对象。
  • GetHashCode(虚):重写 Equals 也需要重写这个方法,用与哈希表的使用。

Dynamic 基元类型

编译器生成使用 dynamic 表达式/变量调用成员(payload代码)的特殊IL代码,表达式/变量调用成员的值动态决定。dynamic 类型类似于 Object,值类型会在必要的时候进行装/拆箱。如果字段、方法参数或返回值类型是dynamic编译器会在元数据中的字段、参数或返回类型应用 System.Runtime.CompilerServices.DynamicAttribute 的实例,局部变量不会应用 DynamicAttributevar是编译的时候推到类型,var不能用于推到来自 dynamic 的类型。

dynamic 的一个限制是只能访问对象的实例成员,如果需要访问静态成员。可以从 System.Dynamic.DynamicObject 派生一个类,后者实现了 IDyanmicMetaObjectProvider 的接口来实现一个动态绑定。

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Basic
{
    /// <summary>
    /// 通过反射获取静态字段
    /// </summary>
    public sealed class StaticMemberDynamicWrapper: System.Dynamic.DynamicObject
    {
        /// <summary>
        /// 表示类型的信息
        /// </summary>
        private readonly TypeInfo m_type;
        public StaticMemberDynamicWrapper(Type type):base()
        {
            m_type = type.GetTypeInfo();
        }
        /// <summary>
        /// 查找是否存在一个静态的方法
        /// </summary>
        /// <param name="name">方法名</param>
        /// <param name="paramTypes">参数列表的类型</param>
        /// <returns></returns>
        private MethodInfo FindMethod(String name, Type[] paramTypes)
        {
            return m_type.DeclaredMethods.FirstOrDefault((MethodInfo m) =>
            {
                return m.IsStatic && m.IsPublic && m.Name.Equals(name) && ParametersMatch(m.GetParameters(), paramTypes);
            });
        }
        /// <summary>
        /// 参数是否匹配
        /// </summary>
        /// <param name="parameters">函数的参数信息</param>
        /// <param name="paramsTypes">指定的参数Type信息</param>
        /// <returns></returns>
        private Boolean ParametersMatch(ParameterInfo[] parameters, Type[] paramsTypes)
        {
            if (parameters.Length != paramsTypes.Length)
                return false;
            for(Int32 i =0;i<parameters.Length;++i)
            {
                if (parameters[i].ParameterType != paramsTypes[i])
                    return false;
            }
            return true;
        }
        /// <summary>
        /// 返回字段的信息
        /// </summary>
        /// <param name="name">字段名</param>
        /// <returns></returns>
        private FieldInfo FindField(String name)
        {
            return m_type.DeclaredFields.FirstOrDefault(fi => fi.IsPublic &&
            fi.IsStatic && fi.Name.Equals(name));
        }
        /// <summary>
        /// 获取属性的访问器的元信息
        /// </summary>
        /// <param name="name">属性名称</param>
        /// <param name="get">设置get或者set的元信息</param>
        /// <returns></returns>
        private PropertyInfo FindProperty(String name, Boolean get)
        {
            if(get)
                return m_type.DeclaredProperties.FirstOrDefault(pi=>pi.Name.Equals(name) &&
                pi.GetMethod !=null && pi.GetMethod.IsPublic &&
                pi.GetMethod.IsStatic);
            return m_type.DeclaredProperties.FirstOrDefault(pi => pi.Name.Equals(name) &&
                pi.SetMethod != null && pi.SetMethod.IsPublic &&
                pi.SetMethod.IsStatic);
        }
        public override IEnumerable<String> GetDynamicMemberNames()
        {
            return m_type.DeclaredMembers.Select(mi => mi.Name);
        }
        public override Boolean TryGetMember(GetMemberBinder binder, out object result)
        {
            result = null;
            var field = FindField(binder.Name);
            if(field !=null)
            {
                result = field.GetValue(null);//获取值
                return true;
            }
            var prop = FindProperty(binder.Name, true);
            if(prop !=null)
            {
                result = prop.GetValue(null, null);return true;
            }
            return false;

        }
        public override Boolean TrySetMember(SetMemberBinder binder, Object value)
        {
            var field = FindField(binder.Name);
            if (field != null)
            {
                field.SetValue(null, value);
                return true;
            }
            var prop = FindProperty(binder.Name, true);
            if (prop != null)
            {
                prop.SetValue(null, value, null); return true;
            }
            return false;
        }
        public override Boolean TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            MethodInfo method = FindMethod(binder.Name, args.Select(c => c.GetType()).ToArray());
            if(method == null)
            {
                result = null;return false;
            }
            result = method.Invoke(null, args);
            return true;
        }
        public static void Main()
        {
            CodeTime.CodeTimer.Time("反射的性能", 10000, () =>
            {
                dynamic stringType = new StaticMemberDynamicWrapper(typeof(String));
                var v = stringType.Concat("A", "B");
            });
            CodeTime.CodeTimer.Time("不使用反射的性能", 10000, () =>
            {
                //dynamic stringType = new StaticMemberDynamicWrapper(typeof(String));
                var v = String.Concat("A", "B");
            });
        }
    }
}

参考