2011-10-27 5 views
3

DataRowから梱包して開梱します。はい、私たちはORMを使うべきですが、それまではこれが私たちのものです。この種の動作を行うためのより汎用的な方法はありますか?

string username; 

var temp = dr["Username"]; 
if (DbNull.Equals (temp)) 
{ 
    username = "Anonymous"; 
} else { 
    username = dr["Username"].ToString(); 
} 

これはパターンとなり、ヘルパーメソッドに翻訳され、最終的に:

string username = StringExtensions.SafeParse (dr["Username"], "Anonymous"); 

これはまだ面倒であり、その結果、このようなコードがたくさんありますすべての種類のプリミティブに必要な拡張メソッド。また、コードを混乱させる。 As<T>と呼ばれるobjectに汎用拡張メソッドを作成しました。使い方は以下のようになります。

string username = dr["Username"].As<string> ("Anonymous"); 

この比較的単純な変更は、他の開発者と偉大な冷静で満たされている、と多くの場所に慣れています。私が不満を感じているのは、潜在的なパフォーマンスの影響です。 今、私は早めの最適化がないことを知っています。早すぎる最適化を行わずにコードを書いたことは間違いありません。後でそれを最適化することは大したことではありません。私は比較的控えめな2GHzワークステーションで毎秒250万回のコンバージョンを行う方法をベンチマークしました。これは他の開発者を保存する時間と読みやすさの向上に比べて驚異的なパフォーマンスです。しかし、以下のコードのサンプルを見ると、私は言語の機能を誤っているように感じ、もっとうまくいくかもしれません。このメソッドはxmldocされています。 "大声で叫ぶ"と大声で叫びます!私はダブルボクシングを避けるためのより良い方法を探しています。私が簡潔にするために省略した実際のバージョンは、多くの場合、実際にはTryParseを使用しています。

public static TDestination As<TDestination> (this object source, TDestination defaultValue = default(TDestination)) 
{ 
    if (source is TDestination) 
     return (TDestination) source; 

    if (source == null || DbNull.Equals(source)) 
     return defaultValue; 

    if (TDestination is int) 
     return (TDestination) (object) Convert.ToInt32 (source.ToString()); 

    if (TDestination is long) 
     return (TDestination) (object) Convert.ToInt64 (source.ToString()); 

    if (TDestination is short) 
     return (TDestination) (object) Convert.ToInt16 (source.ToString()); 

    // and so on... 
} 
+0

あなたの ''コードサンプルはコンパイルされません。 – phoog

答えて

3

はなぜチェックしていません、そして、それがある場合、ToTypeを使用します。

var convertible = source as IConvertible; 
if (convertible != null) 
    return (TDestination)convertible.ToType(typeof(TDestination), Thread.CurrentThread.CurrentUICulture); 
+0

私はこれが好きです。 –

3

あなたの質問に与えられた例As法に基づいて、あなただけの代わりにこれを行うことができます:

public static TDestination As<TDestination> 
    (this object source, TDestination defaultValue = default(TDestination)) 
{ 
    if ((source == null) || Convert.IsDBNull(source)) 
     return defaultValue; 

    return (TDestination)source; 
} 
+0

このようなストレートキャストは、どのような変換動作も行いますか、それとも暗黙のキャストのみに依存していますか? –

+0

@insta:いいえ、そうではありません。しかし、あなた自身の 'As'メソッドは' Convert.ToInt32'を呼び出す前に 'if(source int)'チェックを行い、 'Convert.ToInt64'を呼び出す前に' if(source is long) 'チェックを行います。 'ソース'はボックス化された 'int'であり、' ToString'を呼び出し、 'Convert.ToInt32'の後に' object'へのボクシングキャストが続くことを既に知っています。 'TDestination'にキャストします。 ( '' long''、 '' short''などと同様に)なぜ、無意味な往復を避け、直接 'TDestination'にキャストするのはなぜですか? – LukeH

+0

ああ、それは私が質問を台無しにしたためです。私のコードは実際にはゲームを少し変更するソースではなく、typeof(TDestination)のチェ​​ックを行います。 –

2

を私は反射や私のジェネリッククラスのTを確認するに入るたびに、私はするつもりです辞書Dictionary<Type, ???>を使用してください。私がいつも何かを入れておく価値は、毎回FuncまたはActionとしてください。あなたのケースでは、私はそのように多分それを記述します。

public static class MyConverter 
{ 
    private static Dictionary<Type, Func<object, object>> _MyConverter; 

    static MyConverter() 
    { 
     _MyConverter = new Dictionary<Type, Func<object, object>>(); 

     // Use the Add() method to include a lambda with the proper signature. 
     _MyConverter.Add(typeof(int), (source) => Convert.ToInt32 (source.ToString())); 

     // Use the index operator to include a lambda with the proper signature. 
     _MyConverter[typeof(double)] = (source) => Convert.ToDouble(source.ToString()); 

     // Use the Add() method to include a more complex lambda using curly braces. 
     _MyConverter.Add(typeof(decimal), (source) => 
     { 
      return Convert.ToDecimal(source.ToString()); 
     }); 

     // Use the index operator to include a function with the proper signature. 
     _MyConverter[typeof(float)] = MySpecialConverterFunctionForFloat; 
    } 

    // A function that does some more complex conversion which is simply unreadable as lambda. 
    private static object MySpecialConverterFunctionForFloat(object source) 
    { 
     var something = source as float?; 

     if (something != null 
      && something.HasValue) 
     { 
      return something.Value; 
     } 

     return 0; 
    } 

    public static TDestination As<TDestination>(this object source, TDestination defaultValue = default(TDestination)) 
    { 
     // Do some parameter checking (if needed). 
     if (source == null) 
      throw new ArgumentNullException("source"); 

     // The fast-path exit. 
     if (source.GetType().IsAssignableFrom(typeof(TDestination))) 
      return (TDestination)source; 

     Func<object, object> func; 

     // Check if a converter is available. 
     if (_MyConverter.TryGetValue(typeof(TDestination), out func)) 
     { 
      // Call it and return the result. 
      return (TDestination)func(source); 
     } 

     // Nothing found, so return the wished default. 
     return defaultValue; 
    } 
} 

objectの使用量は機能が、多くの場合のために繰り返し呼び出された場合、おそらくいくつかのパフォーマンスの問題を作る(アン)ボクシングにつながることが、このアプローチの唯一の欠点非常に短時間でしかし、いつものようには、請求する前に測定してです。

反対側では、コンバータを追加するのは簡単ですし、辞書の使用により常にO(1)になります。

+0

これは素晴らしいですね。 Visual Studioにアクセスしてテストしていますか? 'TDestination'についてはわからないので、コンストラクタに問題があることが分かりました。辞書の初期化を 'As'メソッド自体に移すことは、キャッシングの目的を破るようです。( –

0

どのようにあなたがそうのように、フィールドと同じタイプの独自のヌル値を供給することができたDataRowフィールドのプロパティの拡張メソッドを定義について:あなたのオブジェクトがIConvertibleある場合

 public static T Field<T>(this DataRow row, string columnName, T nullValue) { return !row.IsNull(columnName) ? row.Field<T>(columnName) : nullValue; } 
0

私はフィールドの機能のバリエーションを作ることに行くための最善の方法であることに同意していますが、パフォーマンスを懸念している場合、その後のIsNull()または使用しません実際のフィールド機能は多くの冗長チェックを実行するためです。以下の方法はあなたが本当に必要なものです。

public static T Field<T>(this DataRow row, string columnName, T nullValue) 
{ 
    object value = row[columnName]; 
    return ((DBNull.Value == value) ? nullValue : (T)value); 
} 

これは、追加ボクシングが発生する必要性を除去して、あなたはあなたが一般的に関数を呼び出すときに、明示的にTを指定する必要が見送る可能性がNULLVALUEパラメータを使用する方法に注意している場合。 win-win。

+0

拡張メソッドはDataRowに固有のものではありません。汎用キャスティングとデータ変換の方法として –

+0

その場合は、前述の静的コンストラクタメソッドが最も柔軟なソリューションですが、かなりのコストがかかります。パフォーマンスに本当に関心がある場合は、 ASの機能は、私があなたの最も一般的なケースに提供したものと同じです。この方法で、できるだけ最適化することができます。 – 0rigin

関連する問題