ホームページ  >  記事  >  バックエンド開発  >  C#アセンブリとリフレクションのグラフィックコードを詳しく解説

C#アセンブリとリフレクションのグラフィックコードを詳しく解説

黄舟
黄舟オリジナル
2017-03-29 11:45:381845ブラウズ

この記事では主にC#のアセンブリとリフレクションに関する知識を紹介します。一定の基準値があるので、編集者と一緒に見てみましょう

ここでさらに一言言います。本を読んだり、ビデオを見たりするとき、誰もがとても楽しいと感じます。実際、それを理解することと、自分でそれを言えるようになるのは別のことです。他人の真似をするのではなく、学んだことを自分のものにしていく必要があります。

リフレクションについて話す前に、まずアセンブリとは何なのかを理解しましょう。

Assembly

Assembly は、.net の概念であり、Java の jar パッケージに相当する、一連の関連クラスのパッケージと見なされます。

アセンブリには以下が含まれます:

  • リソース ファイル

  • 型メタデータ (コード内で定義された各型とメンバーをバイナリ形式で説明)

  • IL コード (これらは exe または dll 媒体にパッケージ化されています)

exeとdllの違い。

exeは実行できますが、exe内にmain関数(エントリ関数)があるため、dllを直接実行することはできません。

タイプ メタデータ この情報は、AssemblyInfo.cs ファイルを通じてカスタマイズできます。すべての .net プロジェクトには AssemblyInfo.cs ファイルがあり、コード形式:

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的常规信息通过以下
// 特性集控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("ReflectedDemo")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ReflectedDemo")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 使此程序集中的类型
// 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
// 则将该类型上的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("7674d229-9929-4ec8-b543-4d05c6500863")]
// 程序集的版本信息由下面四个值组成: 
//
//   主版本
//   次版本 
//   生成号
//   修订号
//
// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
// 方法是按如下所示使用“*”: 
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

この情報はどこに反映されますか?これはアセンブリのプロパティに反映されます

通常、いくつかの CS クライアント プログラムをインストールすると、インストール ディレクトリの下に多くのアセンブリ ファイルが表示されます。

アセンブリを使用する利点

    プログラム内で必要なアセンブリのみが参照されるため、プログラムのサイズが削減されます。
  • アセンブリは一部のコードをカプセル化し、必要なアクセス インターフェイスのみを提供できます。
  • 簡単に拡張できます。
アセンブリに参照を追加するにはどうすればよいですか?

アセンブリ パスを直接追加するか、ソリューションにプロジェクト参照を追加します。

プログラムを拡張する必要がある場合、元のプロジェクトに直接追加できます。この場合、コードを他の人と共有したい場合はどうすればよいでしょうか。これをアセンブリにパッケージ化すると、他のユーザーがアセンブリを参照して拡張できます。 log4net、unity などの一般的な .net サードパーティ

framework

ライブラリと同様です。

注:

循環 参照を追加することはできません

循環参照の追加とは何ですか?つまり、プロジェクト A がプロジェクト B にプロジェクト参照を追加する場合、現時点ではプロジェクト B はプロジェクト A にプロジェクト参照を追加できません。つまり、プロジェクト参照を追加する場合は、次のように一方向である必要があります。共通の 3 層プロジェクトの参照。

リフレクション

リフレクションについては、.net開発を行っている限り、毎日使用する必要があります。 VS のスマート プロンプトは、リフレクション テクノロジと、一般的に使用される逆コンパイル アーティファクト Reflector.exe を適用して実装されているため、その名前からわかります。プロジェクトでは、データベース インスタンスの切り替えや、構成ファイルによる Sprint.net の依存関係注入など、構成ファイルを組み合わせてオブジェクトを動的にインスタンス化することがより一般的です。

Reflection テクノロジは、実際にはアセンブリのメタデータを動的に取得し、DLL を解析してオブジェクトを作成し、メンバーを呼び出す機能です。

Type はクラスの説明です。Type クラスは、メソッド、属性などを含むクラス内のすべての情報を取得できる、リフレクションを実装するための重要なクラスです。クラスのプロパティとメソッドを動的に呼び出すことができます。

過去にはオブジェクトは新しいものを通じて直接作成されていたため、リフレクションの出現によりオブジェクトの作成方法が変わりました。

DLL には、IL 中間言語とメタデータ要素データの 2 つの部分があります。

.NET でリフレクションに使用される

名前空間

は、System.Reflection です。ここでは、最初にデモを使用してリフレクションで何ができるかを確認します 1. 新しいコンソール プロジェクト ReflectedDemo を作成します

2. 新しい

クラス ライブラリを作成します。

プロジェクトMy.Sqlserver.DalSqlServerHelperとSqlCmdの2つの新しいクラスを作成します。前者はパブリッククラスであり、後者はプライベートクラスです

namespace My.Sqlserver.Dal
{
  public class SqlServerHelper
  {
    private int age = 16;
    public string Name { get; set; }
    public string Query()
    {
      return string.Empty;
    }
  }
  class SqlCmd
  {
  }
}

3. My.Sqlserver.Dalのプロジェクト参照を追加します。この目的は、My.Sqlserver.Dal.dll アセンブリが常に ReflectedDemo プロジェクトの bin ディレクトリに存在すると便利です。

using System;
using System.Reflection;
namespace ReflectedDemo
{
  class Program
  {
    static void Main(string[] args)
    {
      //加载程序集文件,在bin目录中查找
      Assembly assembly = Assembly.Load("My.Sqlserver.Dal");
      Console.WriteLine("----------------Modules----------------------");
      var modules = assembly.GetModules();
      foreach(var module in modules)
      {
        Console.WriteLine(module.Name);
      }
      Console.WriteLine("----------------Types----------------------");
      var types = assembly.GetTypes(); //获取程序集中所有的类型,包括公开的和不公开的
      foreach(var type in types)
      {
        Console.WriteLine(type.Name);
        Console.WriteLine(type.FullName);
        var members= type.GetMembers(); //获取Type中所有的公共成员
        Console.WriteLine("----------------members----------------------");
        foreach(var m in members)
        {
          Console.WriteLine(m.Name);
        }
      }
      Console.WriteLine("----------------GetExportedTypes----------------------");
      var exportedTypes = assembly.GetExportedTypes(); //获取程序集中所有的公共类型
      foreach(var t in exportedTypes)
      {
        Console.WriteLine(t.Name);
      }
      Console.WriteLine("----------------GetType----------------------");
      var typeName= assembly.GetType("SqlServerHelper");//获取程序集中指定名称的类型对象
      Console.WriteLine(typeName.Name);
    }
  }
}

オブジェクトを動的に作成する

通过ass.CreateInstance(string typeName) 和Activator.CreateInstance(Type t)方法

他们之间的区别

ass.CreateInstance(string typeName) 会动态调用类的无参构造函数创建一个对象,返回值就是创建的对象,如果没有无参构造函数就会报错。


Assembly assembly = Assembly.Load("My.Sqlserver.Dal");
object obj = assembly.CreateInstance("My.Sqlserver.Dal.SqlServerHelper");
Console.WriteLine(obj.GetType().ToString());

如果我们来修改SqlServerHelper类的代码,添加如下构造函数:

    public SqlServerHelper(int age)
    {
      this.age = age;
    }

这个时候再来运行创建实例的代码就会报错了,而编译时是不报错的。

所以我们一般推荐使用Activator.CreateInstance方法来创建反射对象,因为此方法有许多重载,支持将参数传递给构造函数。

此时再调用就不会出现异常了。

Type类中有三个用得比较多的方法:

  • bool IsAssignableFrom(Type t):是否可以从t赋值,判断当前的类型变量是不是可以接受t类型变量的赋值。

  • bool IsInstanceOfType(object o):判断对象o是否是当前类的实例,当前类可以是o的类、父类、接口

  • bool IsSubclassOf(Type t):判断当前类是否是t的子类

Type类中还有一个IsAbstract属性:判断是否为抽象的,包含接口。

它们常用的原因是我们通过反射可以取到的东西太多了,我们需要对数据进行过滤。

添加类BaseSql,让类SqlServerHelper继承自BaseSql

然后查看调用代码:

      bool result = typeof(BaseSql).IsAssignableFrom(typeof(SqlServerHelper));
      Console.WriteLine(result);


   SqlServerHelper _SqlServerHelper = new SqlServerHelper(1);
   bool result = typeof(SqlServerHelper).IsInstanceOfType(_SqlServerHelper);
   Console.WriteLine(result);


      SqlServerHelper _SqlServerHelper = new SqlServerHelper(1);
      bool result = typeof(SqlServerHelper).IsSubclassOf(typeof(BaseSql));
      Console.WriteLine(result);

项目中常用的利用反射来动态切换数据库Demo:

新建类库项目My.Sql.IDal,并添加接口ISqlHelper。通过接口来实现数据库操作的类的解耦,因为接口是抽象的。

  public interface ISqlHelper
  {
    string Query();
  }

添加类库项目My.MySql.Dal,并新增类MySqlHelper.cs

My.Sqlserver.Dal、My.MySql.Dal项目分别添加对项目My.Sql.IDal的引用。让SqlServerHelper继承自接口ISqlHelper

public class MySqlHelper : ISqlHelper
  {
    public string Query()
    {
       return this.GetType().ToString();
    }
  }
  public class SqlServerHelper :ISqlHelper
  {
    private int age = 16;
    public string Name { get; set; }
    public string Query()
    {
      return this.GetType().ToString();
    }
  }

添加App.config配置项

 <appSettings>
  <add key="DBName" value="My.Sqlserver.Dal,SqlServerHelper"/>
 </appSettings>

ReflectedDemo项目中Program.cs调用代码:

string str = ConfigurationManager.AppSettings["DBName"];
      string strAssembly = str.Split(&#39;,&#39;)[0];
      string strClass=str.Split(&#39;,&#39;)[1];
      Assembly assembly = Assembly.Load(strAssembly);
      Type t = assembly.GetType(strAssembly + "." + strClass);
      ISqlHelper obj = Activator.CreateInstance(t) as ISqlHelper;
      Console.WriteLine(obj.Query());

这样每次需要切换数据库时,只要修改配置文件就可以了。

项目结构:

注意:反射虽然很强大,但却是比较耗性能的,所以一般和缓存结合起来使用。

以上がC#アセンブリとリフレクションのグラフィックコードを詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。