.NETでAction
とFunc
を使用する方法を知っていますが、開始するたびに、代わりに呼び出す通常の古いメソッドを使用して、まったく同じソリューションを実現できます。
これは、LINQの.Where
のように、私が制御できないものの引数としてAction
またはFunc
が使用されている場合を除外します。
だから基本的に私の質問は...なぜこれらが存在するのですか?単純な方法では得られない、特別で新しいものは何ですか?
ここでの他の回答は、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です(これ以上で独自のパラメーターを定義できないわけではありません)が メソッドはサポートします)必要以上に。
どちらをいつ使用するかについては、次のことを考慮します。
上記のいずれかに基づいて使用を余儀なくされた場合、とにかく他の選択肢はありません。 ポイント3は、私が見つけた最も説得力のあるものであり、それに基づいて決定を下す必要があります。
ほとんどの場合、通常の方法が最適です。これは、C#およびVB.NETの世界で共通の機能のセットをリファクタリングする標準的な方法です。
経験則として、関数が1行以上の場合は、メソッドを使用します。
関数が特定のメソッドの外部に関連性がなく、関数が単純なセレクター(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);
}
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
とは無関係であり、使用方法が少し異なることを忘れないでください。 関連する質問についてはこれも参照してください。
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#言語の非常に強力な機能です。
それらを使用して関数の配列を作成します。たとえば、実行可能なアクションでいっぱいの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のテキストに基づいて呼び出すメソッドを決定するよりもはるかに簡単です。
Funcがメソッドが役に立たない場合に役立つ場合はたくさんあります。
public void DoThing(MyClass foo, Func<MyClass, string> func)
{
foo.DoSomething;
var result = func(foo);
foo.DoStringThing(result);
}
したがって、このメソッドを呼び出すときはいつでも、別のFuncを指定できます。DoThing
メソッドは、何が行われているのかを知る必要はなく、文字列が返されるだけです。
代わりにdelegate
キーワードを使用することにより、Funcキーワードを使用せずにこれを行うことができます。それはほとんど同じように機能します。
action
とfunc
の優れた使用法の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;
}