web-dev-qa-db-ja.com

アクション/ファンクvsメソッド、ポイントは何ですか?

.NETでActionFuncを使用する方法を知っていますが、開始するたびに、代わりに呼び出す通常の古いメソッドを使用して、まったく同じソリューションを実現できます。

これは、LINQの.Whereのように、私が制御できないものの引数としてActionまたはFuncが使用されている場合を除外します。

だから基本的に私の質問は...なぜこれらが存在するのですか?単純な方法では得られない、特別で新しいものは何ですか?

32
James P. Wright

ここでの他の回答は、Action/Funcとは何かとその使用法について話していると思います。 Action/Funcとメソッドのどちらを選択するかについてお答えします。最初の違い:

1)生のパフォーマンスの観点から、 デリゲートは直接メソッド呼び出しと比較して遅い 、しかしそれはそれほど重要ではないので心配するそれについては悪い習慣です。

2)メソッドはオーバーロード(異なる署名を持つ同じ関数名)を持つことができますが、Action/Funcデリゲートは変数として宣言されており、C#ルールではできないためできません特定のスコープ内に同じ名前の2つの変数があります。

bool IsIt() { return 1 > 2; }
bool IsIt(int i) { return i > 2; } //legal

Func<bool> IsIt = () => 1 > 2; 
Func<int, bool> IsIt = i => i > 2; //illegal, duplicate variable naming

3)したがって、Action/Funcは再割り当て可能であり、任意の関数を指すことができますが、一度コンパイルされたメソッドは永久に同じままです。 Func/Actionを使用することは、それが指すメソッドが実行時に決して変更されない場合、意味的に間違っています。

bool IsIt() { return 1 > 2; } //always returns false

Func<bool> IsIt = () => 1 > 2; 
IsIt = () => 2 > 1; //output of IsIt depends on the function it points to.

4)通常のメソッドのref/outパラメーターを指定できます。たとえば、あなたは持つことができます

bool IsIt(out string p1, ref int p2) { return 1 > 2; } //legal

Func<out string, ref int, bool> IsIt; //illegal

5)Action/Funcの新しいジェネリック型パラメーターを導入することはできません(これらはすでにジェネリックですが、型引数は既知の型のみにすることができますメソッドとは異なり、親メソッドまたはクラスで指定されます)。

bool IsIt<A, R>() { return 1 > 2; } //legal

Func<bool> IsIt<A, R> = () => 1 > 2; //illegal

6)メソッドには、Action/Funcではなく、オプションのパラメーターを含めることができます。

bool IsIt(string p1 = "xyz") { return 1 > 2; } //legal

Func<string, bool> IsIt = (p1 = "xyz") => 1 > 2; //illegal

7)メソッドのパラメーターにはparamsキーワードを使用できますが、Action/Funcでは使用できません。

bool IsIt(params string[] p1) { return 1 > 2; } //legal

Func<params string[], bool> IsIt = p1 => 1 > 2; //illegal

8)Intellisenseは、メソッドのパラメーター名でうまく機能します(したがって、メソッドで使用できるクールなXMLドキュメントがあります)。Action/Funcではそうではありません。読みやすさに関する限り、通常の方法が優先されます。

9)Action/Funcのパラメーター制限は16です(これ以上で独自のパラメーターを定義できないわけではありません)が メソッドはサポートします)必要以上に。

どちらをいつ使用するかについては、次のことを考慮します。

  1. 上記のいずれかに基づいて使用を余儀なくされた場合、とにかく他の選択肢はありません。 ポイント3は、私が見つけた最も説得力のあるものであり、それに基づいて決定を下す必要があります。

  2. ほとんどの場合、通常の方法が最適です。これは、C#およびVB.NETの世界で共通の機能のセットをリファクタリングする標準的な方法です。

  3. 経験則として、関数が1行以上の場合は、メソッドを使用します。

  4. 関数が特定のメソッドの外部に関連性がなく、関数が単純なセレクター(Func<S, T>)や述語(Func<bool>)のように些細なものである場合は、Action/Funcをお勧めします。たとえば、

    public static string GetTimeStamp() 
    {
        Func<DateTime, string> f = dt => humanReadable 
                                       ? dt.ToShortTimeString() 
                                       : dt.ToLongTimeString();
        return f(DateTime.Now);
    }
    
  5. Action/Funcがより理にかなっている状況があるかもしれません。たとえば、重い式を作成してデリゲートをコンパイルする必要がある場合は、一度だけ実行して、コンパイルされたデリゲートをキャッシュする価値があります。

    public static class Cache<T> 
    { 
        public static readonly Func<T> Get = GetImpl();
    
        static Func<T> GetImpl()
        {
            //some expensive operation here, and return a compiled delegate
        }
    }
    

    の代わりに

    public static class Cache<T> 
    {
        public static T Get()
        {
            //build expression, compile delegate and invoke the delegate
        }
    }
    

    最初のケースでは、Getを呼び出すと、GetImplは1回だけ実行されますが、2番目のケースと同様に、(高価な)Getは毎回呼び出されます。


匿名メソッド自体は 特定の制限Func/Actionとは無関係であり、使用方法が少し異なることを忘れないでください。 関連する質問についてはこれも参照してください。

30
nawfal

ActionとFuncは、フレームワークが提供する デリゲート タイプです。デリゲートを使用すると、関数を変数のように扱うことができます。つまり、(とりわけ)メソッドからメソッドに関数を渡すことができます。 C++でプログラミングしたことがある場合は、デリゲートを、参照するメソッドのシグネチャによって制限される関数ポインタと考えることができます。

ActionとFuncは、具体的には、最も一般的なシグネチャのいくつかを備えたジェネリックデリゲート(型パラメーターを受け取ることを意味します)です。ほとんどのプログラムのほとんどすべてのメソッドは、これら2つのいずれかを使用して表すことができるため、次のようなデリゲートを手動で定義する時間を大幅に節約できます。バージョン2より前の.netで実行しました。実際、プロジェクトでこのようなコードを見ると、通常、プロジェクトが.net1.1から移行されたと安全に推測できます。

// This defines a delegate (a type that represents a function)
// but usages could easily be replaced with System.Action<String>
delegate void SomeApplicationSpecificName(String someArgument);

デリゲートをもう少し調べることをお勧めします。これらは、C#言語の非常に強力な機能です。

23
Chris Shain

それらを使用して関数の配列を作成します。たとえば、実行可能なアクションでいっぱいのComboBoxがある場合があります。 ComboBoxにクラスまたは構造のアイテムを入力します。

public class ComboBoxAction
{
    private string text;
    private Action method;

    public ComboBoxAction(string text, Action method)
    {
        this.text = text;
        this.method = method;
    }

    public override string ToString()
    {
        return this.text;
    }

    public void Go()
    {
        this.method();
    }
}

その後、誰かがアイテムを選択すると、アクションを呼び出すことができます。

CType(ComboBox1.SelectedItem, ComboBoxAction).Go()

これは、SelectステートメントでComboBoxのテキストに基づいて呼び出すメソッドを決定するよりもはるかに簡単です。

2
Hand-E-Food

Funcがメソッドが役に立たない場合に役立つ場合はたくさんあります。

public void DoThing(MyClass foo, Func<MyClass, string> func)
{
    foo.DoSomething;
    var result = func(foo);
    foo.DoStringThing(result);
}

したがって、このメソッドを呼び出すときはいつでも、別のFuncを指定できます。DoThingメソッドは、何が行われているのかを知る必要はなく、文字列が返されるだけです。

代わりにdelegateキーワードを使用することにより、Funcキーワードを使用せずにこれを行うことができます。それはほとんど同じように機能します。

2
Kirk Broadhurst

actionfuncの優れた使用法の1つは、メソッドが何であるかに関係なく、(メソッドの前または後に)何らかの操作を実行する必要がある場合です。たとえば、例外が発生した場合、メソッドを10回再試行する必要があります。

次のメソッドを検討してください–その戻り値の型はgenericです。したがって、funcに任意の戻り値の型で適用できます。

public static T ExecuteMultipleAttempts<T>(Func<T> inputMethod, Action additionalTask, int wait, int numOfTimes)
        {
            var funcResult = default(T);
            int counter = 0;
            while (counter < numOfTimes)
            {
                try
                {
                    counter++;
                    funcResult = inputMethod();

                    //If no exception so far, the next line will break the loop.
                    break;
                }
                catch (Exception ex)
                {
                    if (counter >= numOfTimes)
                    {
                        //If already exceeded the number of attemps, throw exception
                        throw;
                    }
                    else
                    {
                        Thread.Sleep(wait);
                    }

                    if (additionalTask != null)
                    {
                        additionalTask();
                    }
                }
            }

            return funcResult;
        }
1
LCJ