🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

3回答

486閲覧

C# デリゲートについて

KGMIRROR

総合スコア12

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

2クリップ

投稿2019/09/20 06:22

前提・実現したいこと

Findの第二引数がデリゲートなのはつい先ほどわかったのですが、引数がどう取られているのかわからず、教えていただきたいです。

発生している問題・エラーメッセージ

Array.Findの第二引数のProductGT10とありますが、ProductGT10の引数がどのように決定されているのか仕組みがわかりません

該当のソースコード

C#

1using System; 2using System.Drawing; 3 4public class Example 5{ 6 public static void Main() 7 { 8 // Create an array of five Point structures. 9 Point[] points = { new Point(100, 200), 10 new Point(150, 250), new Point(250, 375), 11 new Point(275, 395), new Point(295, 450) }; 12 13 // Find the first Point structure for which X times Y 14 // is greater than 100000. 15 Point first = Array.Find(points, ProductGT10); 16 17 // Display the first structure found. 18 Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y); 19 } 20 21 // Return true if X times Y is greater than 100000. 22 private static bool ProductGT10(Point p) 23 { 24 return p.X * p.Y > 100000; 25 } 26} 27// The example displays the following output: 28// Found: X = 275, Y = 395

試したこと

デリゲートに関してしばらく調べました。しかし、コードの理屈が理解できませんでした

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

Array.Find のソース を見てください。

C#

1public static T Find<T>(T[] array, Predicate<T> match) { 2 if( array == null) { 3 throw new ArgumentNullException("array"); 4 } 5 6 if( match == null) { 7 throw new ArgumentNullException("match"); 8 } 9 Contract.EndContractBlock(); 10 11 for(int i = 0 ; i < array.Length; i++) { 12 if(match(array[i])) { 13 return array[i]; 14 } 15 } 16 return default(T); 17}

ここで、match に ProductGT10 が渡されているので、match(x) は ProductGT10(x) と同じです。置き換えると次のようになります。

C#

1private static bool ProductGT10(Point p) 2{ 3 return p.X * p.Y > 100000; 4} 5 6public static Point Find<Point>(Point[] array) { 7 if( array == null) { 8 throw new ArgumentNullException("array"); 9 } 10 Contract.EndContractBlock(); 11 12 for(int i = 0 ; i < array.Length; i++) { 13 if(ProductGT10(array[i])) { 14 return array[i]; 15 } 16 } 17 return default(Point); 18}

投稿2019/09/20 07:25

Zuishin

総合スコア28669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

質問者さんがアップされたコードは Visual Studio 2008 の MSDN ライブラリのサンプルコードですが、その MSDN ライブラリの説明は読まれたでしょうか?

docs.microsoft.com にはその説明は見つからないので、以下に抜粋しておきます。

"Predicate<T> は、渡されたオブジェクトがデリゲートで定義された条件と一致した場合にtrue を返すメソッドのデリゲートです。array の要素は、それぞれ Predicate<T> に渡されます。この処理は、Array 内の先頭の要素から、最後の要素まで繰り返し行われます。一致する要素が見つかった時点で処理が終了します。"

"Predicate<T> デリゲートと Find<T> ジェネリック メソッドを使用して、Point 構造体の配列を検索するコード例を次に示します。X フィールドと Y フィールドの積が 100,000 を超える場合は、デリゲートが表す ProductGT10 メソッドから true が返ります。Find<T> メソッドでは、配列の各要素に対してデリゲートが呼び出され、テスト条件を満たす最初のポイントが返されます。"

ProductGT10の引数がどのように決定されているのか仕組みがわかりません

上の説明によれば、Find<T> メソッドは、第 1 引数として渡された配列 Points[] の各要素 Point を、配列の先頭から最後まで順番に、第 2 引数として渡されたデリゲートを通じで ProductGT10 メソッドに引数として渡し、ProductGT10 メソッドが true を返した時点で作業を終了して、その時の Point を Find<T> メソッドの戻り値として返しているということです。

【追記】

質問者さんの疑問は Array.FInd<T> メソッドよりも C# のデリゲートとは何かということのようにも思えますので、自分的に分かりやすかった Microsoft のチュートリアルのコードを貼っておきます。これを読めばデリゲートの基本は分かるのではないかと思います。

この記事は今はもうリンク切れになていて見ることができないのが残念。

using System; using System.Collections; // デリゲートのチュートリアル // C# のデリゲートは、C や C++ の関数ポインタと同じ。 // C や C++ の関数ポインタは静的関数だけを参照できるのに対し、デリゲートでは静的メソッドと // インスタンスメソッドの両方を参照できる。 namespace Example1 { // int 型の引数を一つ持つメソッドへの参照型を MyDelegate と定義 delegate void MyDelegate(int i); // MyDelegate p; で参照型 p を宣言できる。 // new MyDelegate(Function) でメソッド Function への参照を生成できる。 class Program { // TaskADelegate を呼んで、その引数としてメソッド DelegateFunction への参照値を渡す public static void Main() { TakesADelegate(new MyDelegate(DelegateFunction)); // DelegateFunction(21); と同じ } // ポインタ SomeFunction が指すメソッドを呼んで、それに引数として 21 を渡す public static void TakesADelegate(MyDelegate SomeFunction) { SomeFunction(21); } public static void DelegateFunction(int i) { System.Console.WriteLine("Called by delegate with number: {0}.", i); } } } // 例2:デリゲートは静的メソッドとインスタンスメソッドの両方を参照できることの例。 namespace Example2 { delegate void MyDelegate(); // 引数を持たないメソッドへの参照型を MyDelegate と定義 public class MyClass { public void InstanceMethod() { Console.WriteLine("A message from the instance method."); } static public void StaticMethod() { Console.WriteLine("A message from the static method."); } } public class MainClass { static public void Main() { MyClass p = new MyClass(); MyDelegate d = new MyDelegate(p.InstanceMethod); d(); // d はインスタンスメソッド p.InstanceMethos() への参照値を持つ。 // d() はメソッドそのもの d = new MyDelegate(MyClass.StaticMethod); // 静的なので p.StaticMethod は不可 d(); // 静的メソッド MyClass.StaticMethod() を呼ぶ } } } // 例3:デリゲートの宣言方法、インスタンス化方法、および呼び出し方法を示す。 // デリゲートを使用すると、書店データベースとクライアントコードの機能の分担を適切に行うこと // ができる。BookTextClient コードは、書籍の在庫状況や Booksore コードがペーパーバックを検 // 索する方法については関知しない。Bookstore コードは、ペーパーバックの検索後の処理について // は関知しない。 namespace Bookstore { public struct Book { public string Title; // Title of the book. public string Author; // Author of the book. public decimal Price; // Price of the book. public bool Paperback; // Is it paperback? public Book(string title, string author, decimal price, bool paperBack) { Title = title; Author = author; Price = price; Paperback = paperBack; } } // Book 型の引数を一つ持つメソッドへの参照型を ProcessBookDelegate と定義 public delegate void ProcessBookDelegate(Book book); public class BookDB { ArrayList list = new ArrayList(); // データベース中のすべての本のリスト // データベースに本を追加 public void AddBook(string title, string author, decimal price, bool paperBack) { list.Add(new Book(title, author, price, paperBack)); } // データベース BookDB のリスト list にあるすべての本について、 // 本がペーパーバックなら引数 processBook が指す関数を呼ぶ public void ProcessPaperbackBooks(ProcessBookDelegate processBook) { foreach (Book b in list) { if (b.Paperback) processBook(b); } } } } namespace BookTestClient { using Bookstore; // 本の数量と価格合計および平均を求めるためのクラス class PriceTotaller { int countBooks = 0; decimal priceBooks = 0.0m; internal void AddBookToTotal(Book book) { countBooks += 1; priceBooks += book.Price; } internal decimal AveragePrice() { return priceBooks / countBooks; } } class Test { static void PrintTitle(Book b) { Console.WriteLine(" {0}", b.Title); } static void Main() { // Book の配列からなる本のデータベースを作る。bookDB はデータベースを指す BookDB bookDB = new BookDB(); AddBooks(bookDB); Console.WriteLine("Paperback Book Titles:"); // メソッド ProcessPaperbackBooks に処置方法 PrintTitle を渡し bookDB を操作する // (即ち、データベースの本がペーバーバックなら PrintTile する) bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle)); // メソッド ProcessPaperbackBooks に処置方法 AddBookToTotal を渡し bookDB を操作する // (即ち、データベースの本がペーバーバックなら数を数えて価格を合計に加える) PriceTotaller totaller = new PriceTotaller(); bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal)); Console.WriteLine("Average Paperback Book Price: ${0:#.##}", totaller.AveragePrice()); } // テスト用データベースを作るためのメソッド static void AddBooks(BookDB bookDB) { bookDB.AddBook("The C Programming Language", "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true); bookDB.AddBook("The Unicode Standard 2.0", "The Unicode Consortium", 39.95m, true); bookDB.AddBook("The MS-DOS Encyclopedia", "Ray Duncan", 129.95m, false); bookDB.AddBook("Dogbert's Clues for the Clueless", "Scott Adams", 12.00m, true); } } } // 例4:デリゲートの結合方法を示す。 // デリゲートオブジェクトは "+" 演算子を使用して結合できる。結合されたデリゲートは、結合元の複数 // のデリゲートを呼び出す。結合できるのは同じ型のデリゲートだけである。結合されたデリゲートを "-" // 演算子を使用して取り除くこともできる。 namespace Example4 { delegate void MyDelegate(string s); class MyClass { public static void Hello(string s) { Console.WriteLine(" Hello, {0}!", s); } public static void Goodbye(string s) { Console.WriteLine(" Goodbye, {0}!", s); } public static void Main() { MyDelegate a, b, c, d; // デリゲートの宣言 // a にメソッド Hello への参照値を代入 a = new MyDelegate(Hello); // b にメソッド Goodbye への参照値を代入 b = new MyDelegate(Goodbye); // a と b を結合して c に代入 c = a + b; // c から a を取り除いて d に代入 d = c - a; Console.WriteLine("Invoking delegate a:"); a("A"); Console.WriteLine("Invoking delegate b:"); b("B"); Console.WriteLine("Invoking delegate c:"); c("C"); Console.WriteLine("Invoking delegate d:"); d("D"); } } }

投稿2019/09/20 06:53

編集2019/09/20 09:04
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

public delegate bool Predicate<in T>(T obj); として、T(何らかの型)を受け取ってbool型を戻すように基本クラスライブラリに定義されています。

https://docs.microsoft.com/ja-jp/dotnet/api/system.array.find?view=netframework-4.8

https://docs.microsoft.com/ja-jp/dotnet/api/system.predicate-1?view=netframework-4.8

投稿2019/09/20 06:28

ku__ra__ge

総合スコア4524

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問