コードのどこかに、returnステートメントがロックの内側と外側のどこかにあることに気づきました。どれが一番いいですか?
1)
void example()
{
lock (mutex)
{
//...
}
return myData;
}
2)
void example()
{
lock (mutex)
{
//...
return myData;
}
}
どちらを使用すればよいですか?
コードのどこかに、returnステートメントがロックの内側と外側のどこかにあることに気づきました。どれが一番いいですか?
1)
void example()
{
lock (mutex)
{
//...
}
return myData;
}
2)
void example()
{
lock (mutex)
{
//...
return myData;
}
}
どちらを使用すればよいですか?
回答:
基本的に、どちらを使用してもコードが単純になります。出口の単一点は理想的ですが、それを実現するためだけにコードを変形させたりはしません...そして、代替手段がローカル変数(ロック外)を宣言している場合は、初期化(ロック内)およびそれを(ロックの外で)返すと、ロック内の単純な "return foo"の方がはるかに簡単です。
ILの違いを示すために、コードを記述します。
static class Program
{
static void Main() { }
static readonly object sync = new object();
static int GetValue() { return 5; }
static int ReturnInside()
{
lock (sync)
{
return GetValue();
}
}
static int ReturnOutside()
{
int val;
lock (sync)
{
val = GetValue();
}
return val;
}
}
(私は喜んでそれReturnInside
がC#のより単純でよりクリーンなビットであると主張することに注意してください)
そして、IL(リリースモードなど)を見てください。
.method private hidebysig static int32 ReturnInside() cil managed
{
.maxstack 2
.locals init (
[0] int32 CS$1$0000,
[1] object CS$2$0001)
L_0000: ldsfld object Program::sync
L_0005: dup
L_0006: stloc.1
L_0007: call void [mscorlib]System.Threading.Monitor::Enter(object)
L_000c: call int32 Program::GetValue()
L_0011: stloc.0
L_0012: leave.s L_001b
L_0014: ldloc.1
L_0015: call void [mscorlib]System.Threading.Monitor::Exit(object)
L_001a: endfinally
L_001b: ldloc.0
L_001c: ret
.try L_000c to L_0014 finally handler L_0014 to L_001b
}
method private hidebysig static int32 ReturnOutside() cil managed
{
.maxstack 2
.locals init (
[0] int32 val,
[1] object CS$2$0000)
L_0000: ldsfld object Program::sync
L_0005: dup
L_0006: stloc.1
L_0007: call void [mscorlib]System.Threading.Monitor::Enter(object)
L_000c: call int32 Program::GetValue()
L_0011: stloc.0
L_0012: leave.s L_001b
L_0014: ldloc.1
L_0015: call void [mscorlib]System.Threading.Monitor::Exit(object)
L_001a: endfinally
L_001b: ldloc.0
L_001c: ret
.try L_000c to L_0014 finally handler L_0014 to L_001b
}
ILレベルでは、それらは[与えられるか、いくつかの名前を付ける]同一です(私は何かを学びました;-p)。そのため、唯一の賢明な比較は、ローカルのコーディングスタイルの(非常に主観的な)法則です...私ReturnInside
は単純さのために好んでいますが、どちらにもワクワクしません。
ret
.try
違いはありません。どちらもコンパイラによって同じものに変換されます。
明確にするために、どちらかは次のセマンティクスで何かに効果的に変換されます:
T myData;
Monitor.Enter(mutex)
try
{
myData= // something
}
finally
{
Monitor.Exit(mutex);
}
return myData;
私は間違いなくロックの中に戻りを入れます。そうしないと、別のスレッドがロックに入り、returnステートメントの前に変数が変更される危険性があるため、元の呼び出し元が予想とは異なる値を受け取るようになります。
場合によります、
ここで穀物に逆らうつもりです。私は通常、ロックの内側に戻ります。
通常、変数mydataはローカル変数です。初期化中にローカル変数を宣言するのが好きです。ロックの外で戻り値を初期化するデータがほとんどありません。
だからあなたの比較は実際に欠陥があります。理想的には、2つのオプションの違いは、あなたが書いたとおりであり、ケース1にうなずくようですが、実際には少し醜いです。
void example() {
int myData;
lock (foo) {
myData = ...;
}
return myData
}
対
void example() {
lock (foo) {
return ...;
}
}
特に短いスニペットの場合、ケース2はかなり読みやすく、失敗しにくいと思います。
注:この回答は事実として正しいと信じており、それが役に立てば幸いですが、具体的なフィードバックに基づいて改善できることを常に嬉しく思います。
既存の回答を要約して補足するには:
受け入れられた回答にかかわらず、あなたの中で選択した構文形式の、ということを示してC#の -ため、実行時に- ILコード内のコード、return
までは発生しません後にロックが解除されます。
return
内側にlock
ブロックを配置すると、制御の流れが誤って表示されます[1]が、戻り値をauxに格納する必要がなくなるため、構文的には便利です。ローカル変数(ブロックの外側で宣言できるため、ブロックの外側で使用できますreturn
)-Edward KMETTの回答を参照してください。個別に-そしてこの側面は質問に付随的ですが、それでも興味があるかもしれません(Ricardo Villamilの答えはそれを解決しようとしますが、間違っていると思います)- lock
ステートメントをステートメントと組み合わせるreturn
-つまり、return
から保護されたブロックで同時アクセス- 一度取得したものを実際に保護する必要がない場合にのみ、呼び出し側のスコープの戻り値を意味のある方法で「保護」します。これは、次のシナリオに適用されます。
戻り値がコレクションからの要素であり、要素自体の変更や要素の変更ではなく、要素の追加と削除に関してのみ保護が必要な場合
...返される値が値型または文字列のインスタンスである場合。
それ以外の場合は、ロックはメソッド内で(単に)ではなく、呼び出し元が実行する必要があります。
[1] テオドールZouliasはそれが配置するためにも技術的に真であることを指摘するreturn
内部をtry
、catch
、using
、if
、while
、for
文、...。ただし、lock
この質問が尋ねられ、多くの注目を集めたことからも明らかなように、真の制御フローの精査を招く可能性が高いのは、ステートメントの特定の目的です。
[2]値型のインスタンスにアクセスすると、常にスレッドローカルのスタック上のコピーが作成されます。文字列は技術的には参照型のインスタンスですが、値のような型のインスタンスとして効果的に動作します。
lock
、returnステートメントの配置から意味を導き出します。これは私見のこの質問とは無関係の議論です。また、「不当表示」の使用はかなり気になるものです。戻った場合lock
詐称制御の流れを、同じから戻すため言えるtry
、catch
、using
、if
、while
、for
、および言語の任意の他の構築物。これは、C#が制御フローの不正確な表現に悩まされていると言っているようなものです。イエス...
try
、if
私は個人的にはそれについて考えることすらしませんがlock
、特に、私にとって質問が生じました-そしてそれが他の人にも起こらなければ、この質問は決して尋ねられなかっただろう、そして受け入れられた答えは、真の振る舞いを調査するためにそれほど長くは及ばなかっただろう。