web-dev-qa-db-ja.com

Funcとは何か、どのように、いつ使用されるか

Func<>とは何ですか、また何に使用されますか?

104
learning

Func<T>は、T型の値を返すメソッドの定義済みデリゲート型です。

つまり、このタイプを使用して、Tの値を返すメソッドを参照できます。例えば。

public static string GetMessage() { return "Hello world"; }

このように参照される場合があります

Func<string> f = GetMessage;
70
Brian Rasmussen

プレースホルダーと考えてください。特定のパターンに従うが、特定の機能に結び付けられる必要のないコードがある場合に非常に役立ちます。

たとえば、Enumerable.Select拡張メソッドを検討してください。

  • パターンは:シーケンス内のすべてのアイテムについて、そのアイテムから何らかの値(プロパティなど)を選択し、これらの値で構成される新しいシーケンスを作成します。
  • プレースホルダーは、上記のシーケンスの値を実際に取得するセレクター関数です。

このメソッドは、具体的な関数の代わりにFunc<T, TResult>を取ります。これにより、上記のパターンが適用されるanyコンテキストで使用できます。

たとえば、List<Person>があり、リスト内のすべての人の名前だけが必要だとしましょう。私がすることができます:

var names = people.Select(p => p.Name);

または、すべての人の年齢が欲しいと言います:

var ages = people.Select(p => p.Age);

すぐに、2つの異なるパターンSelectを使用)を表すsameコードを活用する方法を確認できます。関数(p => p.Nameおよびp => p.Age)。

別の方法は、異なる種類の値のシーケンスをスキャンするたびに、異なるバージョンのSelectを記述することです。したがって、上記と同じ効果を達成するには、次のものが必要です。

// Presumably, the code inside these two methods would look almost identical;
// the only difference would be the part that actually selects a value
// based on a Person.
var names = GetPersonNames(people);
var ages = GetPersonAges(people);

代理人がプレースホルダーとして機能するため、このような場合に同じパターンを何度も書く必要がなくなります。

80
Dan Tao

Func<T1, T2, ..., Tn, Tr>は、(T1、T2、...、Tn)引数を取り、Trを返す関数を表します。

たとえば、関数がある場合:

double sqr(double x) { return x * x; }

ある種の関数変数として保存できます:

Func<double, double> f1 = sqr;
Func<double, double> f2 = x => x * x;

そして、sqrを使用する場合とまったく同じように使用します。

f1(2);
Console.WriteLine(f2(f1(4)));

等.

ただし、これはデリゲートであり、より詳細な情報についてはドキュメントを参照してください。

61
Grozz

Func<T1,R>およびその他の事前定義されたジェネリックFuncデリゲート(Func<T1,T2,R>Func<T1,T2,T3,R>など)は、最後のジェネリックパラメーターの型を返すジェネリックデリゲートです。

パラメーターに応じて異なる型を返す必要がある関数がある場合、Funcデリゲートを使用して、戻り型を指定できます。

11
Oded

「オンザフライ」でパーソナライズする必要があるコンポーネントを作成する場合、Func<T>は非常に便利です。

この非常に簡単な例を見てみましょう:PrintListToConsole<T>コンポーネント。

このオブジェクトのリストをコンソールに出力する非常にシンプルなオブジェクト。それを使用する開発者に出力のパーソナライズを許可したい場合。

たとえば、特定の種類の数値形式などを定義できるようにします。

機能なし

まず、入力を受け取り、コンソールに出力する文字列を生成するクラスのインターフェイスを作成する必要があります。

interface PrintListConsoleRender<T> {
  String Render(T input);
}

次に、以前に作成したインターフェイスを取得し、リストの各要素で使用するクラスPrintListToConsole<T>を作成する必要があります。

class PrintListToConsole<T> {

    private PrintListConsoleRender<T> _renderer;

    public void SetRenderer(PrintListConsoleRender<T> r) {
        // this is the point where I can personalize the render mechanism
        _renderer = r;
    }

    public void PrintToConsole(List<T> list) {
        foreach (var item in list) {
            Console.Write(_renderer.Render(item));
        }
    }   
}

コンポーネントを使用する必要がある開発者は次のことを行う必要があります。

  1. インターフェースを実装する

  2. 実際のクラスをPrintListToConsoleに渡します

    class MyRenderer : PrintListConsoleRender<int> {
        public String Render(int input) {
            return "Number: " + input;
        }
    }
    
    class Program {
        static void Main(string[] args) {
            var list = new List<int> { 1, 2, 3 };
            var printer = new PrintListToConsole<int>();
            printer.SetRenderer(new MyRenderer());
            printer.PrintToConsole(list);
            string result = Console.ReadLine();   
        }   
    }
    

Funcを使用すると、はるかに簡単になります

コンポーネント内で、タイプFunc<T,String>のパラメーターを定義します。このパラメーターは、タイプTの入力パラメーターを受け取り、ストリング(コンソールの出力)を返す関数のインターフェースを表すです。

class PrintListToConsole<T> {

    private Func<T, String> _renderFunc;

    public void SetRenderFunc(Func<T, String> r) {
        // this is the point where I can set the render mechanism
        _renderFunc = r;
    }

    public void Print(List<T> list) {
        foreach (var item in list) {
            Console.Write(_renderFunc(item));
        }
    }
}

開発者がコンポーネントを使用するとき、彼は単にFunc<T, String>タイプの実装をコンポーネントに渡します。これはコンソールの出力を作成する関数です。

class Program {
    static void Main(string[] args) {
        var list = new List<int> { 1, 2, 3 }; // should be a list as the method signature expects
        var printer = new PrintListToConsole<int>();
        printer.SetRenderFunc((o) => "Number:" + o);
        printer.Print(list); 
        string result = Console.ReadLine();
    }
}

Func<T>を使用すると、その場でジェネリックメソッドインターフェイスを定義できます。入力のタイプと出力のタイプを定義します。シンプルで簡潔。

11
Marco Staffoli

定義済みの汎用デリゲートです。これを使用すると、すべてのデリゲートを宣言する必要はありません。別の定義済みデリゲートAction<T, T2...>があります。これは同じですが、voidを返します。

6

情報を追加するのに遅すぎることはないでしょう。

合計:

Funcは、システムネームスペースで定義されたカスタムデリゲートであり、0〜16の入力パラメーターを使用して、同じシグネチャ(デリゲートと同じ)のメソッドを指すことができ、何かを返す必要があります。

命名法とhow2use:

Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;

定義:

public delegate TResult Func<in T, out TResult>(T arg);

使用場所:

ラムダ式と匿名メソッドで使用されます。

0
overRideKode