16 反射、元数据和动态编程(缺少DLR)

  • 反射、元数据和动态编程
    • 在运行期间检查代码和动态编程
    • 自定义特性
      • 编写自定义特性
      • 自定义特性示例
        • WhatsNewAttributes库程序集
    • 反射
      • System.Type类
        • Type属性
        • 方法
      • TypeView示例
      • Assembly
      • WhatsNewAttributes示例
      • 为反射使用动态语言扩展
        • 创建Calculator库
        • 动态实例化类型
        • 用反射API调用成员
        • 用动态类型调用成员
      • dynamic类型
        • 例子
        • 后台上的动态操作
      • DLR
      • 包含 DLR ScriptRuntime

反射、元数据和动态编程

在运行期间检查代码和动态编程

反射描述了在运行过程中检查和处理程序元素的功能。例如,反射允许完成以下任务

  • 枚举类型的成员
  • 实例化新对象
  • 执行对象的
  • 查找类型的信息
  • 查找程序集的信息
  • 检查应用于某种类型的自定义特性
  • 创建和编译新程序集

自定义特性

自定义特性非常强大的因素是使用反射,代码可以读取这些元数据,使用它们在运行期间作出决策。

小知识:
1. 可以根据StructLayout特性中的信息在内存中布置结构。
2. 自定义特性可以用于支持对自定义许可类进行声明性的代码访问安全检查,把信息与程序元素关联起来,程序元素由测试工具使用,或者在开发可扩展的架构时,允许加载插件或模块。

编写自定义特性

当编译器发现这个属性(property)应用了一个FieldName特性时,首先会把字符串Attribute追加到这个名称的后面,形成一个组合名称FieldNameAttribute,然后在其搜索路径的所有名称空间(即在using语句中提及的名称空间)中搜索有指定名称的类。编译器会找到含有该名称的类,且这个类直接或间接派生自System.Attribute。编译器还认为这个类包含控制特性用法的信息。特别是属性类需要指定:

  • 特性可以应用到哪些类型的程序元素上(类、结构、属性和方法等)
  • 它是否可以多次应用到同一个程序元素上
  • 特性在应用到类或接口上时,是否由派生类和接口继承
  • 这个特性有哪些必选和可选参数

[FieldName(“SocialSecurityNumber”)] 等价于 [FieldNameAttribute(“SocialSecurityNumber”)]

自定义特性示例

WhatsNewAttributes的程序集

  • WhatsNewAttributes程序集,它包含特性的定义。
  • VectorClass程序集,它包含所应用的特性的代码。
  • LookUpWhatsNew程序集,它包含显示己改变的数据项详细信息的项目。

WhatsNewAttributes库程序集

LastModifiedAttribute——特性可以用于标记最后一次修改数据项的时间
SupportsWhatsNewAttribute

反射

System.Type类

获取某个给定类型的Type引用的3种方式

Type t = typeof(double);
double d = 10;
Type t = d.GetType();
Type t = Type.GetType("System.Double");

Type是许多反射功能的入口。它实现许多方法和属性,这里不可能列出所有的方法和属性,而主要介绍如何使用这个类。注意,可用的属性都是只读的:可以使用Type确定数据的类型,但不能使用它修改该类型!

Type属性

属性 返回值
Name 数据类型名
FullName 数据类型的完全限定名(包括名称空间名)
NameSpace 在其中定义数据类型的名称空间名
属性 返回对应的Type引用
BaseType 该Type的直接基本类型
UnderlyingSystemType 该Type在.NET运行库中映射到的类型(某些.NET基类实际上映射到由正识别的特定预定义类型)

方法

返回的对象类型 方法
ConstructorInfo GetConstructor(),GetConstructors()
EventInfo GetEvent(),GetEvents()
FieldInfo GetField(),GetFields()
MemberInfo GetMember(),GetMembers(),GetDefaultMembers()
MethodInfo GetMethod(),GetMethods()
PropertyInfo GetProperty(),GetProperties()

TypeView示例

class Program
{
    /// 
    /// 输出的文本
    /// 
    private static StringBuilder OutputText = new StringBuilder();
    static void Main(string[] args)
    {
        Type t = typeof(double);

        AnalyzeType(t);
        Console.WriteLine($"分析{t.Name}类型");
        Console.WriteLine(OutputText.ToString());

        Console.ReadLine();
    }

    private static void AnalyzeType(Type t)
    {
        TypeInfo typeInfo = t.GetTypeInfo();
        AddToOutput($"Type Name:{t.Name}");
        AddToOutput($"Full Name:{t.FullName}");
        AddToOutput($"NameSpace:{t.Namespace}");

        Type tBase = t.BaseType;
        if (tBase != null)
        {
            AddToOutput($"Base Type:{tBase.Name}");
        }
        AddToOutput("\n公有成员:");

        foreach (MemberInfo member in t.GetMembers())
        {
#if NET46
            AddToOutput($"{member.DeclaringType} {member.MemberType} {member.Name}");
#else
            AddToOutput($"{member.DeclaringType} {member.Name}");
#endif
        }
    }

    private static void AddToOutput(string v)
    {
        OutputText.Append("\n" + v.ToString());
    }
}
分析Double类型

Type Name:Double
Full Name:System.Double
NameSpace:System
Base Type:ValueType

公有成员:
System.Double IsFinite
System.Double IsInfinity
System.Double IsNaN
System.Double IsNegative
System.Double IsNegativeInfinity
System.Double IsNormal
System.Double IsPositiveInfinity
System.Double IsSubnormal
System.Double CompareTo
System.Double CompareTo
System.Double Equals
System.Double op_Equality
System.Double op_Inequality
System.Double op_LessThan
System.Double op_GreaterThan
System.Double op_LessThanOrEqual
System.Double op_GreaterThanOrEqual
System.Double Equals
System.Double GetHashCode
System.Double ToString
System.Double ToString
System.Double ToString
System.Double ToString
System.Double TryFormat
System.Double Parse
System.Double Parse
System.Double Parse
System.Double Parse
System.Double Parse
System.Double TryParse
System.Double TryParse
System.Double TryParse
System.Double TryParse
System.Double GetTypeCode
System.Object GetType
System.Double MinValue
System.Double MaxValue
System.Double Epsilon
System.Double NegativeInfinity
System.Double PositiveInfinity
System.Double NaN

Assembly

加载程序集

Assembly assembly1 = Assembly.Load("SomeAssembly");
Assembly assembly2 = Assembly.LoadFrom(@"C:\\Project\OtherAssembly");

WhatsNewAttributes示例

class Program
{
    /// 
    /// 准备阶段创建的文本
    /// 
    private static readonly StringBuilder outputText = new StringBuilder(1000);
    /// 
    /// 选择的日期
    /// 
    private static DateTime backDateTo = new DateTime(2015,2,1);
    static void Main(string[] args)
        {
            //加载VectorClass程序集
            Assembly theAssembly = Assembly.Load(new AssemblyName("VectorName"));
            //验证它是否真的用SupportsWhatsNew特性标记
            Attribute supportsAttribute = theAssembly.GetCustomAttribute(typeof(SupportsWhatsNewAttribute));
            string name = theAssembly.FullName;
            AddToOutput($"Assembly:{name}");

            if (supportsAttribute == null)
            {
                AddToOutput("程序集不支持WhatsNew Attributes");
                return;
            }
            else {

                AddToOutput("类型:");
            }
            //获取包括在该程序集中定义的所有类型的集合
            IEnumerable types = theAssembly.ExportedTypes;

            foreach (Type definedType in types)
            {
                //给outputText字段添加相关的文本,包括LastModifiedAttribute类的任何实例的详细信息
                DisplayTypeInfo(definedType);
            }

            Console.WriteLine($"What\'s New 从{backDateTo}");
            Console.WriteLine(outputText.ToString());
            Console.ReadLine();
        }

    private static void DisplayTypeInfo(Type type)
        {
            //检查传递的引用是否是一个类
            if (!type.GetTypeInfo().IsClass)
            {
                return;
            }

            AddToOutput($"\nclass{type.Name}");

            IEnumerable attributes = type.GetTypeInfo().GetCustomAttributes().OfType();

            if (attributes.Count()!=0)
            {
                AddToOutput("这个类没有被改变过");
            }
            else
            {
                foreach (LastModifiedAttribute attribute in attributes)
                {
                    WirteAttributeInfo(attribute);
                }
            }

            AddToOutput("这个类被改变过");

            foreach (MethodInfo method in type.GetTypeInfo().GetMethods().OfType())
            {
                IEnumerable attributesToMethods = method.GetCustomAttributes().OfType();

                if (attributesToMethods.Count() > 0)
                {
                    AddToOutput($"{method.ReturnType}{method.Name}()");
                    foreach (Attribute attribute in attributesToMethods)
                    {
                        WirteAttributeInfo(attribute);
                    }
                }
            }
        }

    private static void AddToOutput(string v)
    {
        outputText.Append("\n" + v);
    }

    private static void WirteAttributeInfo(Attribute attribute)
    {
        LastModifiedAttribute lastModifiedAttribute = attribute as LastModifiedAttribute;
        if (lastModifiedAttribute == null)
        {
            return;
        }
        DateTime modifiedDate = lastModifiedAttribute.DateModified;
        //修改日期 < 选择的日期
        if (modifiedDate < backDateTo)
        {
            return;
        }

        AddToOutput($"modified:{modifiedDate:D}:{lastModifiedAttribute.Changes}");

        if (lastModifiedAttribute.Issues != null)
        {
            AddToOutput($"OutStanding issues :{lastModifiedAttribute.Issues}");
        }
    }
}

Vector

[LastModified("2015年6月10日", "updated for C# 6 and .NET Core")]
[LastModified("2010年12月14日", "IEnumerable interface implemented:" + "Vector可以作为一个集合来对待")]
[LastModified("2010年2月10日", "IFormattable interface implemented" + "Vector接受N和VE格式的说明符")]
public class Vector : IFormattable, IEnumerable<double>
{
    public Vector(double x, double y, double z)
    {
        X = x;
        Y = y;
        Z = z;
    }

    public Vector(Vector vector) : this(vector.X, vector.Y, vector.Z)
    {

    }

    public double X { get; }
    public double Y { get; }
    public double Z { get; }

    public override bool Equals(object obj) => this == obj as Vector;

    public override int GetHashCode() => (int)X | (int)Y | (int)Z;

    [LastModified("2010年2月10日", "方法加入格式化")]
    public string ToString(string format, IFormatProvider formatProvider)
    {
        if (format == null)
        {
            return ToString();
        }

        switch (format.ToUpper())
        {
            case "N":
                return "|| " + Norm().ToString() + " ||";
            case "VE":
                return $"( {X:E}, {Y:E}, {Z:E} )";
            case "IJK":
                var sb = new StringBuilder(X.ToString(), 30);
                sb.Append(" i + ");
                sb.Append(Y.ToString());
                sb.Append(" j + ");
                sb.Append(Z.ToString());
                sb.Append(" k");
                return sb.ToString();
            default:
                return ToString();
        }
    }

    [LastModified("6 Jun 2015", "added to implement IEnumerable")]
    public IEnumerator<double> GetEnumerator() => new VectorEnumerator(this);

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 

    public override string ToString() => $"({X} , {Y}, {Z}";

    public double this[uint i]
    {
        get
        {
            switch (i)
            {
                case 0:
                    return X;
                case 1:
                    return Y;
                case 2:
                    return Z;
                default:
                    throw new IndexOutOfRangeException(
                        "Attempt to retrieve Vector element" + i);
            }
        }
    }

    public static bool operator ==(Vector left, Vector right) =>
       Math.Abs(left.X - right.X) < double.Epsilon &&
       Math.Abs(left.Y - right.Y) < double.Epsilon &&
       Math.Abs(left.Z - right.Z) < double.Epsilon;


    public static bool operator !=(Vector left, Vector right) => !(left == right);

    public static Vector operator +(Vector left, Vector right) => new Vector(left.X + right.X, left.Y + right.Y, left.Z + right.Z);

    public static Vector operator *(double left, Vector right) =>
        new Vector(left * right.X, left * right.Y, left * right.Z);

    public static Vector operator *(Vector left, double right) => left * right;

    public static double operator *(Vector left, Vector right) =>
        left.X * right.X + left.Y + right.Y + left.Z * right.Z;

    public double Norm() => X * X + Y * Y + Z * Z;

    [LastModified("2015年6月10日", "实现泛型接口IEnumerable")]
    [LastModified("2010年12月14日", "创建集合的item")]
    private class VectorEnumerator : IEnumerator<double>
    {
        readonly Vector _theVector;      // Vector object that this enumerato refers to 
        int _location;   // which element of _theVector the enumerator is currently referring to 

        public VectorEnumerator(Vector theVector)
        {
            _theVector = theVector;
            _location = -1;
        }

        public bool MoveNext()
        {
            ++_location;
            return (_location <= 2);
        }

        public object Current => Current;

        double IEnumerator<double>.Current
        {
            get
            {
                if (_location < 0 || _location > 2)
                    throw new InvalidOperationException(
                        "The enumerator is either before the first element or " +
                        "after the last element of the Vector");
                return _theVector[(uint)_location];
            }
        }

        public void Reset()
        {
            _location = -1;
        }

        public void Dispose()
        {
            // nothing to cleanup
        }
    }
}

WhatsNewAttributes

/// 
/// 在现实生活中,或许想把特性应用到任何对象上。
/// 为了使代码比较简单,这里仅允许将它应用于类和方法 ,
/// 并允许它多次应用到同一项上(AllowMultiple = true),
/// 因为可以多次修改某一项,每次修改都需要用一个不同的特性实例来标记。
/// 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class LastModifiedAttribute : Attribute
{
    /// 
    /// 
    ///
    /// 
    private readonly DateTime _dateModified;

    private readonly string _changes;

    public LastModifiedAttribute(string dateModified, string changes)
    {
        _dateModified = DateTime.Parse(dateModified);
        _changes = changes;
    }
    /// 
    /// 修改日期
    /// 
    public DateTime DateModified => _dateModified;
    /// 
    /// 描述修改信息
    /// 
    public string Changes => _changes;
    /// 
    /// 描述该数据项的任何重要问题
    /// 
    public string Issues { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly)]
public class SupportsWhatsNewAttribute : Attribute
{

}

为反射使用动态语言扩展

创建Calculator库

创建Calculator库,为了加载。

public class Calculator
{
    public double Add(double x, double y) => x + y;
    public double Subtract(double x, double y) => x - y;
}

将生成的dll,复制到C:/addins目录下

动态实例化类型

private static object GetCalculator(string addinPath)
{
    Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(addinPath);
    Type type = assembly.GetType(CalculatorTypeName);
    return Activator.CreateInstance(type);
}

用反射API调用成员

使用反射调用方法的优点是,类型不需要在编译期间可用。只要把库复制到指定的目录中,就可以在稍后添加它。

private static void ReflectionOld()
{
    double x = 3;
    double y = 4;
    object calc = GetCalculator(CalculatorLibPath);
    object result = calc.GetType().GetMethod("Add").Invoke(calc, new object[] { x, y });

    Console.WriteLine($"{x}加{y}结果:{result}—object");
}

用动态类型调用成员

容易拼写错误。
和强类型方式访问相比,使用dynamic类型会有更多的开销。
语法简单。

private static void ReflectionNew()
{
    double x = 3;
    double y = 4;
    dynamic calc = GetCalculator(CalculatorLibPath);
    double result = calc.Add(x,y);
    Console.WriteLine($"{x}加{y}结果:{result}—dynamic");

}

dynamic类型

例子

Person

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
public void DynamicDemoStart()
{
    dynamic dyn;

    dyn = 100;
    Console.WriteLine(dyn.GetType());
    Console.WriteLine(dyn);

    dyn = "这是个字符串";
    Console.WriteLine(dyn.GetType());
    Console.WriteLine(dyn);

    dyn = new Person() { FirstName = "名", LastName = "姓" };
    Console.WriteLine(dyn.GetType());
    Console.WriteLine($"{dyn.LastName}{dyn.FirstName}");
}

结果

System.Int32
100
System.String
这是个字符串
DynamicConsole.Person
姓名

后台上的动态操作

C#编译器做了许多工作,以支持动态类型。在生成的代码中,会看到对System.Runtime.CompilerServices.CallSite类和System.Runtime.CompiIerSeruces.CallSiteBinder类的引用。

CallSite是在运行期间处理查找操作的类型。在运行期间调用动态对象时,必须找到该对象,看看其成员是否存在。Callsite会缓存这个信息,这样查找操作就不需要重复执行。没有这个过程,循环结构的性能就有问题。

CallSite完成了成员查找操作后,就调用CallSiteBinder()方法。它从CallSite中提取信息,并生成表达式树,来表示绑定器绑定的操作。

显然这需要做许多工作。优化非常复杂的操作时要格外小心。显然,使用dynamic类型是有用的,但它是有代价的。

DLR

使用Dynamic类型的一个重要场景是使用Dynamic Language Runtime (动态语言运行时,DLR)的一部分。DLR是添加到CLR的一系列服务,它允许添加动态语言,如Ruby和Python,并使C#具备和这些动态语言相同的某些动态功能。

DLR位于System.Dynamic名称空间和System.Runtime.ComplierServices名称空间中。为了与IronRuby和IronPython等脚本语言集成,需要安装DLR中额外的类型。这个DLR是IronRuby和IronPython环境的一部分,

包含 DLR ScriptRuntime

你可能感兴趣的:(看书笔记_C#高级编程)