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


115

Func<>に使用されますか?


4
これは、特定の署名を持つデリゲートのショートカットにすぎません。以下の回答を完全に理解するには、デリゲートを理解する必要があります;-)
Theo Lenndorff

2
@Odedの答えでは、それは言うIf you have a function that needs to return different types, depending on the parameters, you can use a Func delegate, specifying the return type.
LCJ

回答:


76

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

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

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

このように参照できます

Func<string> f = GetMessage;

しかし、それは静的な1つの引数の関数を表すこともできます=)
Ark-kun

2
@アークくんいいえ、それは正しくありません。の定義はFunc<T>ですdelegate TResult Func<out TResult>()。引数はありません。Func<T1, T2>1つの引数を取る関数になります。
Brian Rasmussen 2014年

4
いいえ、そうです。static int OneArgFunc(this string i) { return 42; } Func<int> f = "foo".OneArgFunc;。=)
アークくん

1
それは特別な拡張メソッドです。
Brian Rasmussen

それについての唯一の特別なことは、ExtensionCLRではなく、C#/ VB.Netコンパイラによってのみ読み取られる属性です。基本的に、インスタンスメソッド(静的関数とは異なります)には、0番目の「this」パラメーターが隠されています。したがって、1引数のインスタンスメソッドは、2引数の静的関数に非常に似ています。次に、ターゲットオブジェクトと関数ポインタを格納するデリゲートがあります。デリゲートは、最初の引数をターゲットに格納することも、しないこともできます。
アークくん

87

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

たとえば、Enumerable.Select拡張方法を考えてみましょう。

  • パターンは次のとおりです。シーケンス内のすべての項目は、その項目(例えば、プロパティ)からいくつかの値を選択して、これらの値からなる新しいシーケンスを作成するため。
  • プレースホルダがある:実際の配列の値を取得し、いくつかのセレクタ機能は、上述しました。

このメソッドは、Func<T, TResult>具体的な関数の代わりに使用します。これにより、上記のパターンが適用されるあらゆる状況で使用できます。

たとえば、「」がありList<Person>、リストのすべての人の名前だけが欲しいとします。私がすることができます:

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

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

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

すぐに、2つの異なる関数(および)を使用して(を使用して)パターンを表す同じコードをどのように活用できたかを確認できます。Selectp => p.Namep => 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);

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


66

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)));

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


1
優れた答えですが、キーワードをコンパイルするにはstaticが必要です
boctulus 2014

16

私が見つけFunc<T>、私はニーズが「オンザフライ」でパーソナライズすることコンポーネントを作成するときに非常に便利。

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

このオブジェクトのリストをコンソールに出力する非常にシンプルなオブジェクト。これを使用する開発者に出力をパーソナライズさせる必要があります。

たとえば、特定のタイプの数値フォーマットを彼に定義させたいとします。

Funcなし

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

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を使用すると、はるかに簡単です。

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

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>その場でジェネリックメソッドインターフェイスを定義できます。 入力のタイプと出力のタイプを定義します。シンプルで簡潔。


2
このマルコを投稿していただきありがとうございます。それは本当に私を助けました。私はしばらくの間funcを理解しようと努めており、私のプログラミングでも積極的に使用しています。この例では、パスをクリアします。StampaFuncメソッドを追加する必要がありました。元のコードでは省略されていたため、表示できませんでした。
Siwoku Adeola 2017

1
Funcサンプルで行が抜けていると思います。print関数またはStampaFuncの呼び出しはどこにありますか?
Bashar Abu Shamaa

11

Func<T1,R>およびその他の事前に定義された一般的なFunc代表団(Func<T1,T2,R>Func<T1,T2,T3,R>など)は、最後のジェネリックパラメータの型を返す一般的な代表団があります。

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


7

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


0

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

和:

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

命名法と使用方法:

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

定義:

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

使用場所:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.