1. C# の委任
C# のデリゲートは、C または C++ の関数ポインターに似ており、特定のパラメーター リストと戻り値の型を持つメソッドへの参照を表す参照型です。簡単に言えば、デリゲートはメソッドのエージェント/仲介者であり、デリゲートは特定の機能を実現するメソッドを指します。デリゲートはメソッドの抽象化であり、同じパラメーターと戻り値の型を持つ一連のメソッドのアドレスを格納します。デリゲートの使用には、デリゲートの定義と宣言、デリゲートのインスタンス化、およびデリゲートの呼び出しの 3 つの段階が含まれます。デリゲートをインスタンス化するとき、デリゲートのインスタンスは、同じ戻り値の型とパラメーター リストを持つメソッドに関連付けられている必要があります。デリゲートメソッドを通じて呼び出されます。さらに、デリゲートを使用すると、メソッドをパラメータとして他のメソッドに渡すことができます。委任されたイメージの例を次に示します。
某人有三子,让他们各自带一样东西出门,并带回一头猎物。
上面一句话可以理解为父亲对儿子的委托:猎物 办法(工具 某工具)-->delegate 猎物(返回值) 带回猎物(委托名)(工具(参数类型) x)-->delegate int GetValue(int i)
三个人执行委托的方法各不相同
兔子 打猎(工具 弓)-public static int GetValue1(int i){ return i; }
野鸡 买(工具 钱)-public static int GetValue2(int i){ return i*2; }
狼 诱捕(工具 陷阱)-public static int GetValue3(int i){ return i*i; }
1.1 宣言の代表団
delegate <return type> delegate-name(<parameter list>)
- return type 为返回值类型
- delegate-name 为委托的名称
- parameter list 为参数列表
注:デリゲートは、デリゲートと同じシグネチャを持つメソッドを参照できます。つまり、デリゲートが参照できるメソッドは、デリゲートが宣言されたときに決定されます。
public delegate int MyDelegate (string s);
//上面的委托可被用于引用任何一个带有一个单一的 string 参数的方法,并返回一个 int 类型变量。
1.2 デリゲートのインスタンス化
デリゲートを宣言した後、それを使用する場合は、 new キーワードを使用してデリゲートされたオブジェクトを作成し、それを特定のメソッドに関連付ける必要があります。デリゲートを作成する場合、新しいステートメントに渡されるパラメーターは関連付けられたメソッド名であり、メソッド呼び出しのように記述されますが、パラメーターはありません。
public delegate void printString(string s); // 声明一个委托
...
printString ps1 = new printString(WriteToScreen); // 实例化委托对象并将其与 WriteToScreen 方法关联
printString ps2 = new printString(WriteToFile); // 实例化委托对象并将其与 WriteToFile 方法关联
1.3 委任の使用
- Action(arg): 委任されたアクションを呼び出し、パラメータ arg を渡します。
- Action.Invoke(arg): 委任されたアクションを呼び出し、パラメータ arg を渡します。
- 2 つのメソッド間の関係: すべてのデリゲート型について、コンパイラはメソッドを自動的に生成します
invoke
。デリゲート型を使用してパラメータを直接追加することは、Invoke (parameter) のショートカットです。実際、これは Invoke() を呼び出すのと同じです。
using System;
delegate int NumberChanger(int n); // 定义委托
namespace c.biancheng.net
{
class Demo
{
static int num = 10;
public static int AddNum(int p){
num += p;
return num;
}
public static int MultNum(int q){
num *= q;
return num;
}
public static int getNum(){
return num;
}
public static void Calculate(NumberChanger nc, int a)
{
nc(a);
}
static void Main(string[] args){
// 创建委托实例
NumberChanger nc1 = new NumberChanger(AddNum);
// 使用委托对象调用方法
// 等价于 nc1.Invoke(25)
nc1(25);
Console.WriteLine("num 的值为: {0}", getNum());
// 使用委托传递方法作为参数
Calculate(MultNum,5)
Console.WriteLine("num 的值为: {0}", getNum());
Console.ReadKey();
}
}
}
1.4 マルチキャスト委任 (結合委任)
開発では、デリゲートを呼び出すことで複数のメソッドを同時に実行できる状況に遭遇することがあります。そのため、マルチキャスト デリゲートの使用を検討できます。デリゲート オブジェクトには非常に便利なプロパティがあります。つまり、
+
演算子を使用して複数のオブジェクトをデリゲート インスタンスに割り当てることができます。演算子を使用して、-
割り当てられたオブジェクトをデリゲートから削除することもできます。これは、デリゲートが呼び出されたときに順番に呼び出されます。リスト内の代表者。デリゲートのこのプロパティは、デリゲートのマルチキャストとして知られており、マルチキャストとも呼ばれます。デリゲートのこのプロパティを使用すると、デリゲートが呼び出されたときに呼び出されるメソッドのリストを作成できます。注: 同じ種類の注文のみを結合できます。
マルチキャスト委任の本質: デリゲート インスタンスは 1 つのメソッドを指すだけでなく、複数のメソッドを指すこともできます。
概要: マルチキャスト デリゲートは、このパイプラインにロードされたデリゲートが順番に実行される限り、パイプラインと同様のフック メカニズムを提供します。すべて MulticastDelegate を継承するため、すべてのデリゲートはマルチキャスト特性を持ちます。
using System;
delegate int NumberChanger(int n); // 定义委托
namespace c.biancheng.net
{
class Demo
{
static int num = 10;
public static int AddNum(int p){
num += p;
return num;
}
public static int MultNum(int q){
num *= q;
return num;
}
public static int getNum(){
return num;
}
static void Main(string[] args){
// 创建委托实例
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1;
nc += nc2;
// 调用多播
nc(5);
Console.WriteLine("num 的值为: {0}", getNum());
Console.ReadKey();
}
}
}
1.5 機能とアクション
デリゲートを使用する上記のプロセスでは、デリゲートの型をカスタマイズし、このカスタム デリゲートを使用してデリゲート フィールドまたは変数を定義します。.NetFramework3.0 以降、新しい機能が追加されました。C# 言語では、一般的に使用される 2 つのデリゲート、つまり Func と Action が事前定義されており、さらにLambda も導入されており、デリゲートの定義と使用が容易になりました。将来的には C# プログラミングにデリゲートを導入することがより柔軟になります。
(1) アクションの委任
C# ではデリゲート型 Action が事前定義されており、その基本的な特徴は、戻り値なし (void) のメソッドを実行/参照できることです。出力パラメーターを持たないデリゲートのクラスです。
- ネイティブ アクション: public delegate void Action(); パラメータを持たず、値を返さないメソッドをカプセル化します。
- 汎用アクション: public delegate void Action(T arg1,…); パラメータを持ち、値を返さないメソッドをカプセル化します (最大 16 個のパラメータ)
static void printString()
{
Console.WriteLine("Hello World");
}
static void printNumber(int x)
{
Console.WriteLine(x);
}
static void Main(String[] args)
{
//Action基本使用
Action a = printString;
a(); // 输出结果 Hello World
//Action指向有参数的方法
Action<int> b = printNumber; // 定义一个指向 形参为int的函C#数
b(5); // 输出结果 5
}
(2) 関数の委任
デリゲート型 Func も C# で定義済みのデリゲートであり、その基本的な特徴は、戻り値を持つメソッドを実行/参照できることです。Action とは異なり、Func にはジェネリックを含むフォームが 1 つだけあります。
- パラメーターのない Func: パブリック デリゲート TResult Func(); パラメーターを持たないメソッドをカプセル化し、
TResult
パラメーターで指定された型の値を返します。- パラメータ付きの Func: public delegate TResult Func<in T,int T,...,out TResult>(T arg1,T arg2,...); パラメータを持つメソッドをカプセル化し、パラメータで指定された型の値を返します。パラメータ
TResult
。(パラメータは最大16個)- 注: ジェネリック リストの最後のジェネリック パラメータは戻り値の型を表し、それより前のパラメータはすべてパラメータ型です。パラメータの型は、指定されたメソッドのパラメータの型に順番に対応する必要があります。
namespace delegatesample_10._15_
{
class Program
{
static void Main(string[] args)
{
Calculator calculator = new Calculator();
//声明Func
Func<int, int, int> func1 = new Func<int, int, int>(calculator.Add);
Func<int, int, int> func2 = new Func<int, int, int>(calculator.Sub);
int x = 100;
int y = 200;
int z1 = 0;
int z2 = 0;
z1 = func1.Invoke(x, y);
Console.WriteLine(z1);
z1 = func1(x, y);
Console.WriteLine(z1);//都是Add方法的调用,输出两遍。
z2 = func2.Invoke(x, y);
Console.WriteLine(z2);
z2 = func2(x, y);
Console.WriteLine(z2);//都是Sub方法的调用,输出两遍。
}
}
class Calculator
{
public int Add(int a,int b)
{
int result = a + b;
return result;
}
public int Sub(int a,int b)
{
int result = a - b;
return result;
}
}
}
2.C#の型変換
2.1 型チェックとは
- is:オブジェクトが指定された型と互換性があるかどうかを確認します。
object objTest = 11;
if( objTest is int )
{
int nValue = (int)objTest;
}
2.2 として自動変換
- as:互換性のある参照型間の変換を自動的に実行するために使用されます。その場合、as は同じオブジェクトへのnull 以外のターゲット参照を返します。ターゲットの型と互換性がない場合、as 演算子は null を返します。
Employee myEmployee = myObject as Employee;
if (myEmployee != null) {
...
}
注: as を使用して型変換を行う場合、変換対象のオブジェクト型は変換対象の型、または変換対象の型の派生型である必要がありますが、値型データには適用できません。参考: C# でのオブジェクトまたは一般的な型の型変換
3. C# の匿名関数/メソッド
C# では、匿名関数は、名前がなく関数本体のみを持つ関数として単純に理解できます。匿名関数は、コードのブロックをデリゲート パラメーターとして渡すための手法を提供します。これは、デリゲート型が予期される場所で使用できる「インライン」ステートメントまたは式です。匿名関数を使用すると、名前付きデリゲートを初期化したり、名前付きデリゲートをメソッド パラメーターとして渡したりできます。無名関数で戻り値の型を指定する必要はなく、戻り値の型はメソッド本体の return ステートメントから推測されることに注意してください。匿名関数には、ラムダ式と通常の匿名関数が含まれます。
- 通常の匿名関数: delegateキーワードを使用して、宣言するデリゲート インスタンスを作成します。= デリゲート (文字列 str){ 戻り値 str;};
- ラムダ式: = (パラメータリスト入力パラメータ) => { 関数本体式; }; 括弧内の複数の入力パラメータはカンマで区切られます。
class Test
{
delegate void TestDelegate(string s);
static void M(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
//委托实例化形式1:普通的委托new方式
TestDelegate testDelA = new TestDelegate(M);
//委托实例化形式2:普通匿名函数方式
TestDelegate testDelB = delegate(string s) {
Console.WriteLine(s); };
//委托实例化形式3:Lambda表达式方式
TestDelegate testDelC = (x) => {
Console.WriteLine(x); };
testDelA("Hello. My name is M and I write lines.");
testDelB("That's nothing. I'm anonymous and ");
testDelC("I'm a famous author.");
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
4.C#のプロパティ
プロパティ構文はフィールドの自然な拡張です。フィールドは保存場所を定義するために使用されますが、プロパティ定義には(少なくとも 1 つ)の宣言
get
と、set
プロパティ(対応するフィールド)を取得して値を割り当てるためのアクセサーが含まれます。プロパティにアクセスすると、プロパティはフィールドのように動作します。ただし、フィールドとは異なり、プロパティはアクセサーを通じて実装されます。アクセサーは、プロパティにアクセスするとき、または値を割り当てるときに実行されるステートメントを定義するために使用されます。検証、さまざまなアクセシビリティ、遅延評価、またはシナリオに必要な要件は、属性を通じて提供できます。
(1) 従来の属性定義
//传统属性声明
//特点:存取时可做逻辑操作,但代码量多比较繁琐
class Student
{
//字段
private int _age;
private string _name;
private Gender _gender;
//属性访问器
public int Age {
get {
return _age; } set {
_age = value; } }
public string Name {
get {
return _name; } set {
_name = value; } }
public Gender Gender {
get {
return _gender; } set {
_gender = value; } }
}
(2) 属性の自動定義
自動プロパティには、完全に非論理的な get アクセサーと set アクセサーがあります。関数本体はアクセサーの後に書かず、直接セミコロンで終了してください。コンパイラは、この属性をサポートするフィールドの保存場所を自動的に生成します。このフィールドは匿名フィールドと呼ばれ、この属性によってのみアクセスできます。匿名フィールドには名前がないため、コード内でそれを使用することはできません。コンパニオン自動プロパティを通じて間接的にのみ使用できます。
//自动属性声明:编译器会自动把这段代码翻译成上面的完整版本
//特点:减少代码量简化开发,但属性定义时不能做其他逻辑判断只能简单存取
class Student
{
public int Age {
get; set; }
public string Name {
get; set; }
public Gender Gender {
get; set; }
}
(3) アクセシビリティ制御
- アクセス許可
アクセス修飾子はアクセサーの前で使用できますが、個々のアクセサーに配置されるアクセス修飾子は、プロパティ定義に対するアクセス修飾子よりも厳しい制限を提供する必要があります。
public class Person
{
//现在,可以从任意代码访问 FirstName 属性,但只能从 Person 类中的其他代码对其赋值。
public string FirstName {
get; private set; }
// Omitted for brevity.
}
- 読み取り専用/書き込み専用
プロパティの変更方法、つまりアクセサーの宣言を直接制限することもできます。
//声明只读属性
public ICommand CheckCommand
{
get
{
return new RelayCommand(_ =>
{
//do something...
});
}
}
(4) 抽象属性
抽象クラスには、派生クラスで実装する必要がある抽象プロパティを持つことができます。次のプログラムはこれを示しています。
using System;
namespace runoob
{
public abstract class Person
{
public abstract string Name
{
get;
set;
}
public abstract int Age
{
get;
set;
}
}
class Student : Person
{
private string code = "N.A";
private string name = "N.A";
private int age = 0;
// 声明类型为 string 的 Code 属性
public string Code
{
get
{
return code;
}
set
{
code = value;
}
}
// 声明类型为 string 的 Name 属性
public override string Name
{
get
{
return name;
}
set
{
name = value;
}
}
// 声明类型为 int 的 Age 属性
public override int Age
{
get
{
return age;
}
set
{
age = value;
}
}
public override string ToString()
{
return "Code = " + Code +", Name = " + Name + ", Age = " + Age;
}
}
class ExampleDemo
{
public static void Main()
{
// 创建一个新的 Student 对象
Student s = new Student();
// 设置 student 的 code、name 和 age
s.Code = "001";
s.Name = "Zara";
s.Age = 9;
Console.WriteLine("Student Info:- {0}", s);
// 增加年龄
s.Age += 1;
Console.WriteLine("Student Info:- {0}", s);
Console.ReadKey();
}
}
}
5. C# イベント
イベントは委任に基づいて構築されますが、委任とは異なります。委任は動作/アクションであり、イベントはアクション発生→動作処理のプロセスであり、モデルです。イベント (イベント) は基本的に、キーの押下、クリック、マウスの移動などのユーザーのアクション、またはシステムによって生成される通知などの何らかの出来事です。アプリケーションは、イベントが発生したときにそれに応答する必要があります。
例: 「発生→応答」の 5 つの部分: 目覚まし時計が鳴り、シャオミンは起きました。目覚まし時計 (イベント ソース)、ベルを鳴らす (イベント)、シャオ ミン (サブスクライバー)、起床 (イベント処理動作) の 4 つの部分が見やすくなります。サブスクリプションという暗黙的な部分もあります。つまり、Xiao Ming は携帯電話の着信音を購読しており、Xiao Ming は携帯電話が鳴ったときにのみ反応します。他人の携帯電話で、シャオミンが加入していない場合は、鳴ってもシャオミンは応答しません。
5.1 イベントモデル
イベントはクラス内で宣言および生成され、イベント ハンドラーは同じクラスまたは他のクラスのデリゲートを使用して関連付けられます。イベントを含むクラスは、イベントを発行するために使用されます。これは発行者クラスと呼ばれます。イベントを受信する他のクラスはサブスクライバー クラスと呼ばれます。イベントはパブリッシュ-サブスクライブ (パブリッシャー-サブスクライバー) モデルを使用します。
-
パブリッシャーは、イベントとデリゲートの定義を含むオブジェクトです。イベントとデリゲート間の接続もこのオブジェクトで定義されます。パブリッシャ クラスのオブジェクトはこのイベントを呼び出し、他のオブジェクトに通知します。
-
サブスクライバは、イベントを受け入れ、イベント ハンドラを提供するオブジェクトです。パブリッシャー クラスのデリゲートは、サブスクライバー クラスのメソッド (イベント ハンドラー) を呼び出します。
5.2 イベントステートメント
クラス内でイベントを宣言するには、まずイベントのデリゲート型を宣言する必要があります。例えば:
public delegate void BoilerLogHandler(string status);
次に、イベント キーワードでデリゲートをラップして、イベント自体を宣言します。
// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
5.3 イベントの使用
//事件发布者
public class PublishEvent
{
//拥有事件及其委托
public delegate string Display(string str);
public event Display DisplayEvent;
//事件触发器
public void Shows(string str)
{
if (DisplayEvent != null)
{
DisplayEvent(str);
}
}
}
//事件订阅者
public class Listen1
{
//订阅事件的处理/响应方法
public string MakeAlert(string str)
{
Console.WriteLine(str + "Listen1");
return str + "Listen1";
}
}
class Program
{
static void Main()
{
//1.声明事件发布者
PublishEvent pe = new PublishEvent();
//2.声明订阅者
Listen1 l1 = new Listen1();
Listen2 l2 = new Listen2();
//变量l1和l2订阅了事件(当侦听到事件发生时会调用各自的方法响应事件)
pe.DisplayEvent += l1.MakeAlert;
pe.DisplayEvent += l2.ShowMsg;
//触发事件->通知订阅->响应事件
pe.Shows("事件");
Console.ReadKey();
}
}
5.4 注意事項
- イベントの本質: イベントの本質は、特別なデリゲートであるデリゲート フィールドのラッパーです。このパッケージ化は、委任を制限し、オブジェクト内のコミッション インスタンスが外部から無差別に使用されるのを防ぐためのものであり、パッケージ化の重要な機能は非表示にすることです。イベントは、デリゲート インスタンスのほとんどの機能を外部から隠します。
- 制限 1: デリゲートの呼び出しには制限がなく、どこからでも呼び出すことができますが、イベントによって変更されたデリゲートは、それ自体を定義するクラス内でのみ呼び出すことができます。
- 制限 2: デリゲートは、「=」、「+=」、および「-=」を使用して割り当てを示し、関数を追加および削除できます。また、イベント変更されたデリゲートは、独自のクラス シンボルでこれら 3 つのシンボルを使用することもできます (サブクラスも外部クラスに属します)、外部クラスでは、関数の追加と削除に「+=」と「-=」のみを使用でき、直接代入に「=」を使用することはできません。
- 命名規則: イベントのデリゲートを宣言するために使用され、通常はイベント名 + EventHandler という名前が付けられます。通常、デリゲート パラメータはイベント名 + EventHandler の 2 つです (win32API から発展し、長い歴史があります)
- 最初のパラメータ: sender はオブジェクト タイプで、イベント ソース\イベント送信者を表します。
- 2 番目のパラメーター: e は EventArgs 型です。EventArgs クラスは、イベント データを含むクラスの基本クラスを表し、イベント データを含まないイベントの値を提供します。
6. ショートカット
- prop+tab : プロパティを自動的に作成します
- propdp+tab : 依存関係プロパティとそのラッパーを自動的に作成します
- propfull+tab : プロパティとそのフィールドを自動的に作成します
- propa+tab : 追加のプロパティを自動的に作成します