C# - 反射与编译

编译

首先了解下,如何区分编译生成的 .dll的版本
方法1:ILSpy反编译工具

通过 assembly属性,release版本没有或仅有如下一种属性

[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]

而 debug版本,属性较多,示例

[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | 
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

具体参见:https://blog.csdn.net/WPwalter/article/details/80933080

方法2:代码检测

public static class Utils
{
    //新增扩展方法
    public static T GetCustomAttribute<T>(this ICustomAttributeProvider provider)
        where T : Attribute
    {
        var attributes = provider.GetCustomAttributes(typeof(T), false);
        return attributes.Length > 0 ? attributes[0] as T : default(T);
    }

    public enum DllMode { Debug = 0, Release = 1 };
        
    public static DllMode CheckDllMode_1(string _filePath)
    {
        var assembly = Assembly.LoadFile(_filePath);
        var attributes = assembly.GetCustomAttributes(typeof(DebuggableAttribute), false);
        if (attributes.Length > 0)
        {
            var debuggable = attributes[0] as DebuggableAttribute;
            if (debuggable != null) {
                return ((debuggable.DebuggingFlags & DebuggableAttribute.DebuggingModes.Default)
                            == DebuggableAttribute.DebuggingModes.Default)
                ? DllMode.Debug : DllMode.Release;
            } else { return DllMode.Release; }
        } else { return DllMode.Release; }
    }
        
    public static DllMode CheckDllMode_2(string _filePath)
    {
        Assembly ass = Assembly.LoadFile(_filePath);
        DebuggableAttribute att = ass.GetCustomAttribute<DebuggableAttribute>();
        return (att != null && att.IsJITTrackingEnabled) ? DllMode.Debug : DllMode.Release;
    }
        
} 

具体参见:https://www.oschina.net/code/snippet_12_8459

新增扩展方法时,若提示: 缺少编译器要求的成员“system.Runtime.CompilerServices.ExtensionAttribute..ctor”
解决方法,在当前.cs中添加

namespace System.Runtime.CompilerServices {
    public class ExtensionAttribute : Attribute { }
}

反射

类Assembly中Load, LoadFrom, LoadFile方法比较

下面给出2种程序集加载方法

//方法1:直接从DLL路径加载 ok
assembly = Assembly.LoadFrom(assemblyPath);

//方法2:先把DLL加载到内存,再从内存中加载 ok
using (FileStream fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read))
{
	using (BinaryReader br = new BinaryReader(fs))
	{
		byte[] bFile = br.ReadBytes((int)fs.Length);
		br.Close();
		fs.Close();
		assembly = Assembly.Load(bFile);
	}
}

可以将程序集中定义的所有类型暂存备用,调用时指定程序集和方法名即可

// htTypes是Hashtable
foreach (Type tp in assembly.GetTypes()) {
    htTypes.Add(tp.Name, tp);
}                


string className = "Calculator.Calculator"; //程序集.类名
string funName = "Add";                     //方法名
if (TypeTest.htTypes.ContainsKey(className))
{
    var tp = (Type)TypeTest.htTypes[className];
    var func = tp.GetMethod(funName);
    if (null != func)
    {
        func.Invoke(null, new object[] { ... });
    }
}

对于设置或获取字段或属性的值,注意区分静态/非静态

非静态的实例字段或属性,GetValue和SetValue时,第一个参数务必传入实例对象

object obj = Activator.CreateInstance(type, true);

而静态的,直接送null即可。设置时,保险起见可以作类型转换

var v = Convert.ChangeType(value, tp.GetField/GetProperty(_name).FieldType/PropertyType);

注,待加载的程序集可以在配置文件中配置。

  <configSections>
    <section name="assembly" type="System.Configuration.NameValueSectionHandler"/>
  </configSections> 
  
  <!-- key为程序集名称,value表示是否要加载 -->
  <assembly>
    <add key="Calculator.dll" value="1"/>
    <add key="crudHelper.dll" value="1"/>
  </assembly>

代码中动态加载即可

NameValueCollection assemblyList = ConfigurationManager.GetSection("assembly") as NameValueCollection;

.Net框架提供了一个综合性方法:Type.InvokeMember,但是参数较多,慎用。

猜你喜欢

转载自www.cnblogs.com/wjcx-sqh/p/10500484.html