2017-04-21 12 views
2

ヌルでない場合や実行を継続する場合、またはそのようにする方法がある場合、1つのライナーが返されますか? これはいくつかの方法でIF線をコピーするのを避けるためです。どこでももし同じコピー&ペーストを避けるために、それをリファクタリングする方法をそうc#1つのライナーで 'nullでない場合はどうすれば返すのですか?

var error = ValidateStuff(someArg); 
if (error != null) 
{ 
    return error; 
} 
DoOtherStuff(); 

初期のコードは次のでしょうか? 擬似コード

ValidateStuff(someArg) ? return ___ : continue; 
DoSomethingElse(); 
AndMoreStuff(); 

-EDIT- アンさらに単純化した例のようになり、いくつかの回答やコメントで起こっているいくつかの疑問クリアするために:

public string Foo(string arg) 
{ 
    string fuu = GetMeSomething(arg); 

    if(fuu != null) return fuu; 

    ImDoingThings(); 

    return "I did things"; 
} 

を持っている素晴らしいだろうこの:

public string Foo(string arg) 
{ 
    ReturnIfNotNull(GetMeSomething(arg)); 

    ImDoingThings(); 

    return "I did things."; 
} 
+0

この機能のさらに広い範囲は何ですか?あなたはバリデーションを持っているし、何かを行う別のクラスを持っていますか?彼らは彼らの背後に共通の目的を持っていますか? –

+2

できません。コードを最小限に抑えるためにできることは、return文を囲む中括弧を省略することだけです。 – ThePerplexedOne

+3

ほとんどの場合、有効な入力が期待される場合は、 'EnsureStuffValid(someArg)'に何も返さず、stuffが有効でない場合に例外をスローすることができます。 –

答えて

11

確か:

void ValidateStuff(someArg) { 
    if (!validation(someArg)) { 
     throw new ValidationException("Whatever went wrong...", errorDetails); 
    } 
} 

そして、あなたのコード内:

ValidateStuff(someArg); 
DoOtherStuff(); 

がP.S:生産関係のないものは、生産バイナリで終わるしないように、私は多くの場合、#if (DEBUG) [...] #else [...] #endifValidateStuffに汎用コードを兼ね備えています。


警告はどうですか?

私はそのためのいくつかのトリックを使用します。あなたが本当にそれを必要とする場合にのみ、エラーオブジェクトを作成し

  1. を。
  2. 同様に、何かが失敗した場合にだけエラーのリストを作成します。
  3. 簡単にコーディングするために「使用」を使用してください。私は怠惰なコーダーです...これは小さなリスクですが、あなたが使い方を忘れると、あなたは困っています...しかし、私は、このリスクは「ショーと一緒に行き、最初に何か警告があったことを忘れてください」という選択肢よりも優れていると思います。
  4. あなたの警告のためのハンドラがあれば(例外の代わりに)、それを使ってそれを使ってください。
  5. エラーが発生した場合は、ロットを投げてください。さらに原因がなければ、あなたが合うようにあなたがそれを拡張することができます明らかに

...

public class WarningsHandler : IDisposable 
{ 
    private List<WarningErrorBase> errors = null; 

    // Default handler. Remember to use 'using', or otherwise you'll end up 
    // with pain and suffering! 
    public void Dispose() 
    { 
     var errors = FetchValidationResults(); 

     if (errors != null && errors.Count > 0) 
     { 
      throw new ValidationException(errors); 
     } 
    } 

    // Handler if you have a better idea than using an Exception 
    public IEnumerable<Error> FetchValidationResults() 
    { 
     var errors = this.errors; 
     this.errors = null; 
     return errors; 
    } 

    public void Warn(bool condition, Func<Warning> errorBuilder) 
    { 
     if (condition) 
     { 
      if (errors == null) { errors = new List<WarningErrorBase>(); } 
      errors.Add(errorBuilder()); 
     } 
    } 

    public void Error(bool condition, Func<Error> errorBuilder) 
    { 
     if (condition) 
     { 
      if (errors == null) { errors = new List<WarningErrorBase>(); } 
      errors.Add(errorBuilder()); 

      throw new ValidationException(FetchValidationResults()); 
     } 
    } 
} 

どのようにそれを使用するには?

void MyThing() 
{ 
    using (var handler = new WarningsHandler()) 
    { 
     handler.Error(foo == null, "Foo must have a value"); 
     handler.Warn(foo.Count > 2,() => new Warning("You should have less than 2 foo's present."); 
     // etc. 
    } 
} 

さて、もう一つだけのトリック。 :-)

小さなオーバーヘッドでさまざまなエラーメッセージを組み合わせる最後の方法は、yield returnを使用することです。これにより、異なる動作で複数の結果値を返すことができます。null値は無視することができます。

まず私たちは、このためにラッパーの全体の束を必要とする:

// We need some base interface that we can use for return values 
public interface IResult { } 

// We have to wrap normal return values 
public class Result<T> : IResult 
{ 
    public Result(T result) { this.Value = result; } 

    public T Value { get; private set; } 
} 

// A few classes for messages, errors, warnings, ... 
public class Message : IResult 
{ 
    public Message(string format, params object[] args) 
    { 
     this.Text = string.Format(format, args); 
    } 

    public string Text { get; private set; } 

    internal virtual void Handle(List<Message> messages) 
    { 
     messages.Add(this); 
    } 
} 

public class Error : Message 
{ 
    public Error(Exception ex) : 
     base("Uncaught exception: {0}", ex.Message) 
    { } 

    public Error(string format, params object[] args) : 
     base(format, args) 
    { } 

    internal override void Handle(List<Message> messages) 
    { 
     throw new ValidationException(this.Text); 
    } 
} 

// Other wrappers like warnings, etc. 
// Wrapping IEnumerable<IResult> is useful too. 

次に、我々は今、代わりに通常のタイプのIEnumerableをを返す私たちの方法を実行するために、いくつかのヘルパーメソッドが必要です。そのために、基本的に実行、アンラッピング、戻り値を処理するヘルパークラスを追加します。

public static class ExecutionEngine 
{ 
    public static T Execute<T>(this IEnumerable<IResult> method) 
    { 
     List<Message> messages = new List<Message>(); 
     try 
     { 
      foreach (var item in method) 
      { 
       // yield return null is ignored here: 
       if (item != null) 
       { 
        // Handle validation results, messages, etc 
        Message msg = item as Message; 
        if (msg != null) 
        { 
         msg.Handle(messages); 
        } 

        Result<T> returnValue = item as Result<T>; 
        if (returnValue != null) 
        { 
         return returnValue.Value; 
        } 

        // handle other things, error if something's wrong 
       } 
      } 

      throw new Exception("Method finished without a return value."); 
     } 
     catch (ValidationException) 
     { 
      // TODO: handle messages? 

      throw; 
     } 
     catch (Exception ex) 
     { 
      // TODO: handle messages? 

      var error = new Error(ex); 
      error.Handle(messages); 

      throw; // unreachable because Error throws. This is to make sure it all compiles 
     } 
    } 
} 

これをすべて整えば、コード自体はかなりシンプルになり、通常と同じように非常によく似ています。主な違いは、あなたは、単に時々、追加のラッパーで、どこでも「降伏リターン」を追加することです:

public IEnumerable<IResult> MyMethod() 
{ 
    // Delegate validation to somewhere else. You might wrap an IEnumerable<IResult> here: 
    yield return ValidateStuff(someArg); 

    // Information messages, etc 
    yield return new Message("Hello world!"); 

    // You might end up with an Exception you didn't expect... 
    var tmp = new List<int>(); 
    tmp[2] = 2; // oopz... 

    // ... 
    yield return new Result<int>(12); // return 12; 
} 

唯一のものが残っているが、あなたはもうMyMethodを呼び出すことはできませんということです。これは、実行エンジンを使用して簡単に修正できます。

+4

これは' return error'ではなく例外を使用しているOPを正しい方向に向ける。 –

+0

待って、私はそれがC#で回避することができたときに例外を発生させることが良いアイデアだと思った。 私はOPのバージョンが優れていると言っていますが、その理由は何ですか?それは回復可能なものであるときにはっきりとしています。この場合、彼のエラーのリスト。 – thinklarge

+0

@ thinklargeは、コードが意味のある方法でエラーから回復できる場合のみです。それ以外の場合は、例外をスローするのではなく、エラーオブジェクトを返すことは決して良い考えではありません。 –

-1

私が検証する最も良い方法はカスタム例外を投げることです。 (CustomExceptionはExceptionクラスから継承された自分自身のクラスである)例えば

、 私は私のビジネス層でこれを使用します(データベースまたは何か他にモデルを保存する)

public class SomeService 
{ 

    public void DoSomething(some params or some instance of a model class) 
    { 
     Validate(some params or an instance of a model class); 

     // some other codes such as save data in database or etc ... 

    } 

    private void Validate(some params or an instance of a model class) 
    { 
     if(something is wrong) 
      throw new CustomException("X is invalid"); // X is the name of wrong param 
    } 

} 

とプレゼンテーション層に:

try 
{ 
    var model = new someModel{ fill properties of model }; 

    var service = new SomeService(); 
    service.DoSomething(model); 

    DoOtherStuff(); 
} 
catch(CustomException ex) 
{ 
    ShowMessageToUser(ex.Message); 
} 

注:ビジネス例外ではなくカスタム例外を捕捉します。

それは私はこれが役に立てば幸い検証パラメータとモデル

のための私の愛する戦争です。

1

まず、間違った方法でこれを実行する可能性があります。エラーコードを返そうとするのではなく、多くの例外を除外することになります。

しかし、あなたはlambdaを使って擬似コードのようなことをすることができます。あなたが実際には「nullでない場合に返す」と言ってきたことからも、

static Error ReturnIf(Error error, 
    Func<Error, bool> predicate, 
    Func<Error> rest) 
{ 
    return predicate(error) ? error : rest(); 
} 

static Error Test2(bool someArg) 
{ 
    return ReturnIf(ValidateStuff(someArg), error => error != null,() => 
    { 
     DoSomethingElse(); 
     AndMoreStuff(); 
     return null; 
    }); 
} 

またはコードを:それは完全を期すために、あなた特定質問に答える、乱雑取得(およびジェネリックでメシエさえされます)簡略化し、より具体的な:私は示唆していないよ

static Error ReturnIfNotNull(Error error, 
     Func<Error> rest) 
{ 
    return error ?? rest(); 
} 

static Error Test2(bool someArg) 
{ 
    return ReturnIfNotNull(ValidateStuff(someArg)() => 
    { 
     DoSomethingElse(); 
     AndMoreStuff(); 
     return null; 
    }); 
} 

Bart de Smetは広範囲にこの技術を探求しますが、あなたが実際に、このような特定の状況...このよう使用コードにしたいと思います。

関連する問題