ホームページ  >  記事  >  バックエンド開発  >  C# 委任コード例の詳細な説明

C# 委任コード例の詳細な説明

黄舟
黄舟オリジナル
2017-03-07 11:05:171074ブラウズ

この記事ではC#のデリゲートを事例分析を通して詳しく紹介します。エディターで見てみましょう

デリゲートは型です。 C# のデリゲートはオブジェクト指向であり、タイプセーフです。デリゲート インスタンスが作成されると、作成されたインスタンスには複数のメソッドを含めることができる呼び出しリストが含まれます。各メソッドは呼び出し側エンティティと呼ばれます。呼び出し側エンティティは、静的メソッドまたはインスタンス メソッドにすることができます。インスタンス メソッドの場合、呼び出し側エンティティには、インスタンス メソッドが呼び出されたインスタンスが含まれます。デリゲートは、呼び出すメソッドのクラスを考慮せず、呼び出されたメソッドがデリゲートの型と互換性があるかどうかのみを考慮します。 以下はコード例です:

using System;
namespace LycheeTest{
 public delegate void D(int a, int b);
 public class Test {
 public D myDelegate;
 public Test() {
  myDelegate = new D(Show1);
 }
 private static void Show1(int a, int b) {
  Console.WriteLine("方法 Show1 被调用,两个实参相加的值是:{0}", a + b);
 }
 private void Show2(int a, int b) {
  Console.WriteLine("方法 Show2 被调用,两个实参相加的值是:{0}", a + b);
 }
 private void Show3(int a, int b) {
  Console.WriteLine("方法 Show3 被调用,两个实参相加的值是:{0}", a + b);
 }
 }
 public class Program {
 static void Main(string[] args) {
  Test myT = new Test();
  myT.myDelegate(33, 22);
  Console.ReadKey();
 }
 }
}

このコードは、委任の最も単純な形式を示しています。 デリゲート型はクラスの外でもクラス内でも定義できます。 このコードはクラスの外で定義されています。コードの 3 行目はデリゲート型を定義します。デリゲート型のキーワードは delegate であり、キーワードの前にデリゲート型のアクセス許可修飾子が続きます。キーワードの後に​​デリゲート型の戻り型が続きます。これは、デリゲート型と互換性のあるメソッドの戻り型が同じである必要があることを指定します。戻り値の型の後にはデリゲート型の名前が続きます。次に、仮パラメーター リストです。これは、パラメーターの型と、デリゲート型と互換性のあるメソッドの数が同じである必要があることを指定します。コードの 5 行目では、デリゲート型変数を定義しています。これはインスタンス フィールドであり、アクセス許可はパブリックです。デリゲート タイプ フィールドのアクセス権は、デリゲート タイプのアクセス権よりも低いか、デリゲート タイプのアクセス権と同じである必要があることに注意してください。 9、12、15 行目では 3 つのメソッドを定義しています。コードの 9 行目は静的メソッドです。このコードはデリゲートを使用する最も単純な方法を示しているため、静的メソッドのみが使用されます。 6 行目のコンストラクターでは、デリゲート型の変数がインスタンス化されています。デリゲート変数の呼び出しリストにメソッドを追加するには、そのコンストラクターにメソッド名を渡すだけで済みます。これは、呼び出しメソッドをデリゲートに追加する最も基本的な方法です。 21 行目で Test クラスのインスタンスを定義し、22 行目でクラスのデリゲート メンバーを呼び出します。デリゲート メンバーを呼び出すときは、実際のパラメーターをその仮パラメーター リストに渡す必要があります。これは委任を使用する最も基本的な方法です。このコードの実行結果は次のとおりです。

方法 Show1 被调用,两个实参相加的值是:55

デリゲート型を使用する別の方法を紹介します。コード例は次のとおりです。

using System;
namespace LycheeTest {
 public delegate void D(int a, int b);
 public class Test {
 public static void Show1(int a, int b) {
  Console.WriteLine("方法 Show1 被调用,两个实参相加的值是:{0}", a + b);
 }
 public void Show2(int a, int b) {
  Console.WriteLine("方法 Show2 被调用,两个实参相加的值是:{0}", a + b);
 }
 public void Show3(int a, int b) {
  Console.WriteLine("方法 Show3 被调用,两个实参相加的值是:{0}", a + b);
 }
 }
 public class Program {
 static void Main(string[] args) {
  Test myT = new Test();
  D myDelegate = new D(Test.Show1);
  D myDelegate1 = new D(myT.Show2);
  D myDelegate2 = new D(myT.Show3);
  myDelegate(22, 33);
  myDelegate1(33, 44);
  myDelegate2(55, 66);
  Console.ReadKey();
 }
 }
}

このコードは、クラス内のデリゲート型フィールドをキャンセルします。ただし、デリゲート型はクラスとして扱われます。エントリ ポイント メソッドを含むクラスでは、最初の行 17 で Test クラスの変数を定義し、インスタンス化します。クラスのインスタンス メソッドはデリゲートに渡されるため、クラスのインスタンス メソッドを参照するには、その前にクラスのインスタンスが存在している必要があります。 18 行目では、デリゲート型の変数を定義し、それをインスタンス化します。ここで、デリゲートはクラスのメンバーではないため、静的メソッドをそのコンストラクターに渡すときは、クラス名で参照する必要があることに注意してください。 19 行目では、デリゲート型変数も定義しています。インスタンス メソッドを渡すときは、クラスのインスタンスによって参照される必要があります。 20行目は19行目と同じです。メソッドをデリゲートに渡すときは、メソッドの仮パラメータ リストの代わりにメソッド名を渡す必要があります。行 21 から 23 はデリゲートへの呼び出しであり、メソッドの実際のパラメーターが渡されます。このコードの実行結果は次のとおりです。

方法 Show1 被调用,两个实参相加的值是:55 
方法 Show2 被调用,两个实参相加的值是:77 
方法 Show3 被调用,两个实参相加的值是:121

デリゲートのアクセス修飾子

デリゲートがクラスの外にある場合、使用できるアクセス修飾子にはpublicとinternalが含まれます。何も書かれていない場合、デフォルトは内部です。デリゲートがクラス内にある場合、使用できるアクセス修飾子には、public、protected、internal、protected が含まれます

using System;
namespace LycheeTest{
 public class Test {
 protected delegate void D(int a, int b);
 private delegate void D1(int a, int b);
 protected internal delegate void D2(int a, int b);
 internal delegate void D3(int a, int b);
 private D myD;
 private D1 myD1;
 private D2 myD2;
 private D3 myD3;
 public Test() {
  myD = new D(Show1);
  myD1 = new D1(Show1);
  myD2 = new D2(Show1);
  myD3 = new D3(Show1);
 }
 public static void Show1(int a, int b) {
  Console.WriteLine("方法 Show1 被调用,两个实参相加的值是:{0}", a + b);
 }
 public void Show2(int a, int b) {
  Console.WriteLine("方法 Show2 被调用,两个实参相加的值是:{0}", a + b);
 }
 public void Show3(int a, int b) {
  Console.WriteLine("方法 Show3 被调用,两个实参相加的值是:{0}", a + b);
 }
 public void Use() {
  myD(11, 12);
  myD1(22, 45);
  myD2(55, 78);
  myD3(345, 100);
 }
 }
 class Test1: Test {
 private D Test1D;
 private D2 Test1D2;
 private D3 Test1D3;
 public Test1() {
  Test1D = new D(Test.Show1);
  Test1D2 = new D2(Test.Show1);
  Test1D3 = new D3(Test.Show1);
 }
 public void Use1() {
  Test1D(22, 45);
  Test1D2(44, 45);
  Test1D3(77, 78);
 }
 }
 public class Program {
 static void Main(string[] args) {
  Test1 myT1 = new Test1();
  myT1.Use();
  myT1.Use1();
  Console.ReadKey();
 }
 }
}

代码的第 4 行在类的内部定义了委托类型,它作为类的成员定义,访问权限是 protected,它可以被本类内部访问,也可以被派生类访问。代码的第 5 行定义的委托类型,访问权限是 private 的,它只可以被本类内部访问。代码的第 6 行定义的 protected internal 访问权限的委托类型,可以被本程序集访问, 还可以被派生类访问,而不管派生类位于哪个程序集。第 7 行定义的委托类型是 internal 的,它只可以被本程序集访问。因为所有这几种委托类型都可以被本类内部访问,所以第 10 行到第 13 行定义了它们的变量。第 12 行的实例构造方法中,对这四个委托类型的变量进行了实例化,并为它们的调用列表加入了方法 Show1。Show1 是一个静态方法,但是在类内部传入委托类型的构造方法时,不需要使用类名引用。第 27 行定义了实例方法,在方法内部调用了这四个委托,并为其传入实参。第 34 行代码又定义了一个类,它继承自基类 Test。因为基类中的委托类型只有 D、D2 和 D3 可以被派生类访问,所以第 35 行到第 37 行定义了它们的变量。注意,虽然它们和基类中的委托变量是同一种类型, 但是它们是不同的委托。在第 38 行的实例构造方法中,为这三个委托类型的变量创建实例,并为其调用列表加入方法,因为静态方法 Show1 也被派生类所继承,所以这里传入的方法名,可以使用类名引用,也可以不使用类名引用。 第 43 行定义了一个实例方法,方法内部调用了这三个委托,并为其传入实参。第 51 行定义了派生类的实例,然后调用实例方法Use和Use1。这段代码的执行结果如下:

方法 Show1 被调用,两个实参相加的值是:23 
方法 Show1 被调用,两个实参相加的值是:67 
方法 Show1 被调用,两个实参相加的值是:133
方法 Show1 被调用,两个实参相加的值是:445 
方法 Show1 被调用,两个实参相加的值是:67 
方法 Show1 被调用,两个实参相加的值是:89 
方法 Show1 被调用,两个实参相加的值是:155

因为 D 和 D2 的访问权限被定义成了 protected 和 protected internal。所以下面来验证在其它程序集中是否可以访问它们。首先要将本段代码中的包含 Main 方法的类去掉,然后在它的项目属性中将它改变为类库。接下来新建一个控制台项目,并物理上引用这个类库。控制台项目的代码如下:

using System;
using LycheeTest;
namespace LycheeTest1{
 class Program: Test {
 private D pD;
 private D2 pD2;
 public Program() {
  pD = new D(Show1);
  pD2 = new D2(Show1);
 }
 public void Use3() {
  pD(34, 33);
  pD2(12, 11);
 }
 static void Main(string[] args) {
  Program p = new Program();
  p.Use3();
  Console.ReadKey();
 }
 }
}

因为第 3 行代码的命名空间和类库的命名空间是两个独立的命名空间,它们的成员不位于同一个命名空间内。所以在一个命名空间内引用另一个命名空间的成员时,需要加上另一个命名空间的名称进行引用。 为了代码编写的方便,第 2 行代码首先引用了类库的命名空间。第 4 行代码定义了一个类,它继承自基类 Test。因为是派生类,所以对于委托类型 D 和 D2 都可以访 问。第 5 行代码和第 6 行代码分别定义了 D 和 D2 的两个变量。第 7 行的实例构造方法对这两个变量进行了实例化,并为其传入方法 Show1。因为 Show1 方法被继承了下来,所以这里不需要类名引用。第 11 行代码定义了一个实例方法,它的作用是调用这两个委托,并为其传入实参。第 16 行代码定义了本类的一个实例,并调用了实例方法 Use3。这段代码的执行结果如下:

方法 Show1 被调用,两个实参相加的值是:67
方法 Show1 被调用,两个实参相加的值是:23

类Test中的委托类型D2和D3都具有internal权限,现在来验证一下,对于一个同一程序集中的非派生类是否可以访问它们。首先将类库更改回控制台项目,然后增加一个类,这个类对于Test类来说是独立的。它们之间只是位于一个程序集内,彼此没有继承关系。代码如下:

using System;
namespace LycheeTest {
 public class Test {
 protected delegate void D(int a, int b);
 private delegate void D1(int a, int b);
 protected internal delegate void D2(int a, int b);
 internal delegate void D3(int a, int b);
 private D myD;
 private D1 myD1;
 private D2 myD2;
 private D3 myD3;
 public Test() {
  myD = new D(Show1);
  myD1 = new D1(Show1);
  myD2 = new D2(Show1);
  myD3 = new D3(Show1);
 }
 public static void Show1(int a, int b) {
  Console.WriteLine("方法 Show1 被调用,两个实参相加的值是:{0}", a + b);
 }
 public void Show2(int a, int b) {
  Console.WriteLine("方法 Show2 被调用,两个实参相加的值是:{0}", a + b);
 }
 public void Show3(int a, int b) {
  Console.WriteLine("方法 Show3 被调用,两个实参相加的值是:{0}", a + b);
 }
 public void Use() {
  myD(11, 12);
  myD1(22, 45);
  myD2(55, 78);
  myD3(345, 100);
 }
 }
 class Test1 {
 private Test.D2 tD2;
 private Test.D3 tD3;
 public Test1() {
  tD2 = new Test.D2(Test.Show1);
  tD3 = new Test.D3(Test.Show1);
 }
 public void Use3() {
  tD2(34, 33);
  tD3(22, 21);
 }
 }
 public class Program {
 static void Main(string[] args) {
  Test1 myT1 = new Test1();
  myT1.Use3();
  Console.ReadKey();
 }
 }
}

这段代码中,原来的类Test没有进行修改。在第35行上,定义了一个类,它是一个相对于Test类来说独立的类。它们的关系仅限于同在一个程序集内。第 36 行代码和第 37 行代码定义了委托类型D2和D3的两个变量。这里需要注意,因为这两个类不是继承关系,所以要引用Test类中的这两个委托类型需要使用Test类的类名进行引用。第 38 行代码是实例构造方法,在构造方法中将委托实例化。实例化委托类型的时候,仍然需要使用类名引用委托类型名,传递的方法名也是如此。第 行42 定义了一个实例方法,它调用了委托,并为其传入了实参。第 49 行代码定义了类Test1的一个实例,然后第 61 行调用类的实例方法。这段代码的执行结果如下:

方法 Show1 被调用,两个实参相加的值是:67
方法 Show1 被调用,两个实参相加的值是:43

 以上就是c# 委托代码实例详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!



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