C#でジェネリックメソッドを作成する


84

私はたくさんの同様のメソッドを一般的なメソッドに結合しようとしています。クエリ文字列の値を返すメソッドがいくつかあります。クエリ文字列が存在しないか、正しい形式でない場合はnullを返します。すべての型がネイティブにnull許容であれば、これは十分に簡単ですが、整数と日付にはnull許容のジェネリック型を使用する必要があります。

これが私が今持っているものです。ただし、数値が無効な場合は0が返されます。残念ながら、これは私のシナリオでは有効な値です。誰かが私を助けることができますか?ありがとう!

public static T GetQueryString<T>(string key) where T : IConvertible
{
    T result = default(T);

    if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
    {
        string value = HttpContext.Current.Request.QueryString[key];

        try
        {
            result = (T)Convert.ChangeType(value, typeof(T));  
        }
        catch
        {
            //Could not convert.  Pass back default value...
            result = default(T);
        }
    }

    return result;
}

彼は多数の実装を移植しているので、古い機能を呼び出し、結果を記憶し、新しい機能を呼び出し、結果を記憶し、比較します。ランダムな入力をたくさん使って100回実行すると、出来上がりです。
Hamish Grubijan 2010年

申し訳ありませんが、この場合、それがどのように適用されるのかまだわかりません。私はまだ関数を動作させようとしています。
マイクコール

答えを見て、私は少し混乱しています:あなたの発信者はintまたはintを使用してパラメータ化していますか?Tとして?

これを内部で処理するのではなく、メソッドが例外をスローできるようにする必要があるように思われます。それは私だけかもしれませんが、ChangeType失敗したときに生成される例外が表示されないため、呼び出しが常にデフォルト値を返す理由が混乱する可能性があります。
2015

回答:


64

default(T)を使用する代わりに、返すデフォルト値を指定した場合はどうなりますか?

public static T GetQueryString<T>(string key, T defaultValue) {...}

それはそれを呼び出すことも簡単にします:

var intValue = GetQueryString("intParm", Int32.MinValue);
var strValue = GetQueryString("strParm", "");
var dtmValue = GetQueryString("dtmPatm", DateTime.Now); // eg use today's date if not specified

欠点は、無効な/欠落しているクエリ文字列値を示すためにマジック値が必要になることです。


はい、これは整数のデフォルト値に依存するよりも実行可能であるように思われます。私はこれを心に留めておきます。非ジェネリック関数を使用することに頼るかもしれませんが、私はまだ元の関数をすべてのタイプで機能させることを望んでいます。
マイクコール

無効な整数に対してゼロ以外のものを返さないのはなぜですか?有効な値ではないか、nullなどの特別な目的をすでに持っているものを返すことができます。InvalidIntegerなどと呼ばれる独自のタイプを作成することもできます。不正なクエリ文字列に対してnullを返していますよね?無効な整数に対してもそれを返すことができるので、nullは単に「何かが悪いので、私には価値がない」ことを意味し、関数を参照してreasonCodeを渡す可能性がありますか?
Dan Csharpster 2014年

1
値を取得する方法:long ? testデフォルトはnullである必要があります
Arshad 2014

16

私は知っています、私は知っています、しかし...

public static bool TryGetQueryString<T>(string key, out T queryString)

4
Try-patternはよく任意の.NET開発者に知られている必要があります。あなたが私に尋ねればそれは悪いことではありません。F#またはNET 4.0では、オプション(または選択)を使用します
Christian Klauser

6
他の理由がない場合、特にそれが使用されることさえない場合は、その出力変数を「事前に宣言」する必要がないため、それを避けようとします。そうでなければ完全に優れたコード行であった可能性があるものの無駄です。
ジェイ

実際には、これが問題を解決する最も簡単な方法です。上記のような1つの関数+この関数を使用する2つのヘルパー(4つのライナーになります)を定義します。
greenoldman 2010年

1
ジェイが述べたのと同じ理由で、私はトライパターンが嫌いです。可能であれば、1つのジェネリック関数を使用したいと思います。これが、私の当初の目標でした。
マイクコール

11
するかしないか、試してはいけません!<ヨーダ>
うさぎ

12

これはどうですか?戻り値の型をからTに変更しますNullable<T>

public static Nullable<T> GetQueryString<T>(string key) where T : struct, IConvertible
        {
            T result = default(T);

            if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
            {
                string value = HttpContext.Current.Request.QueryString[key];

                try
                {
                    result = (T)Convert.ChangeType(value, typeof(T));  
                }
                catch
                {
                    //Could not convert.  Pass back default value...
                    result = default(T);
                }
            }

            return result;
        }

エラー:ジェネリック型またはメソッド「System.Nullable <T>」でパラメーター「T」として使用するには、型「T」はNULL不可の値型である必要があります。
マイクコール

また、を指定する必要がありますwhere T : struct
アーロノート2010年

@Mike C:同じエラーが発生することはないはずです。編集されたコードは間違いなくコンパイルされます。
アーロノート2010年

うん、今それを手に入れました。では、これをString型に対して呼び出したい場合はどうなりますか?今のところ受け入れられません。
マイクコール

@MikeC、それは価値stringがあるので可能だとは思わないnullable
Graviton

5

たぶんモナドのようなものを使うことができます(ジェイの答えが好きですが)

public class Maybe<T>
{
    private readonly T _value;

    public Maybe(T value)
    {
        _value = value;
        IsNothing = false;
    }

    public Maybe()
    {
        IsNothing = true;
    }

    public bool IsNothing { get; private set; }

    public T Value
    {
        get
        {
            if (IsNothing)
            {
                throw new InvalidOperationException("Value doesn't exist");
            }
            return _value;
        }
    }

    public override bool Equals(object other)
    {
        if (IsNothing)
        {
            return (other == null);
        }
        if (other == null)
        {
            return false;
        }
        return _value.Equals(other);
    }

    public override int GetHashCode()
    {
        if (IsNothing)
        {
            return 0;
        }
        return _value.GetHashCode();
    }

    public override string ToString()
    {
        if (IsNothing)
        {
            return "";
        }
        return _value.ToString();
    }

    public static implicit operator Maybe<T>(T value)
    {
        return new Maybe<T>(value);
    }

    public static explicit operator T(Maybe<T> value)
    {
        return value.Value;
    }
}

あなたの方法は次のようになります:

    public static Maybe<T> GetQueryString<T>(string key) where T : IConvertible
    {
        if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
        {
            string value = HttpContext.Current.Request.QueryString[key];

            try
            {
                return (T)Convert.ChangeType(value, typeof(T));
            }
            catch
            {
                //Could not convert.  Pass back default value...
                return new Maybe<T>();
            }
        }

        return new Maybe<T>();
    }

4

Convert.ChangeType().NET 2.0 BCLではnull許容型または列挙型を正しく処理しません(ただし、BCL 4.0では修正されていると思います)。外部の実装をより複雑にするのではなく、コンバーターに多くの作業を行わせます。これが私が使用する実装です:

public static class Converter
{
  public static T ConvertTo<T>(object value)
  {
    return ConvertTo(value, default(T));
  }

  public static T ConvertTo<T>(object value, T defaultValue)
  {
    if (value == DBNull.Value)
    {
      return defaultValue;
    }
    return (T) ChangeType(value, typeof(T));
  }

  public static object ChangeType(object value, Type conversionType)
  {
    if (conversionType == null)
    {
      throw new ArgumentNullException("conversionType");
    }

    // if it's not a nullable type, just pass through the parameters to Convert.ChangeType
    if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
    {
      // null input returns null output regardless of base type
      if (value == null)
      {
        return null;
      }

      // it's a nullable type, and not null, which means it can be converted to its underlying type,
      // so overwrite the passed-in conversion type with this underlying type
      conversionType = Nullable.GetUnderlyingType(conversionType);
    }
    else if (conversionType.IsEnum)
    {
      // strings require Parse method
      if (value is string)
      {
        return Enum.Parse(conversionType, (string) value);          
      }
      // primitive types can be instantiated using ToObject
      else if (value is int || value is uint || value is short || value is ushort || 
           value is byte || value is sbyte || value is long || value is ulong)
      {
        return Enum.ToObject(conversionType, value);
      }
      else
      {
        throw new ArgumentException(String.Format("Value cannot be converted to {0} - current type is " +
                              "not supported for enum conversions.", conversionType.FullName));
      }
    }

    return Convert.ChangeType(value, conversionType);
  }
}

次に、GetQueryString <T>の実装は次のようになります。

public static T GetQueryString<T>(string key)
{
    T result = default(T);
    string value = HttpContext.Current.Request.QueryString[key];

    if (!String.IsNullOrEmpty(value))
    {
        try
        {
            result = Converter.ConvertTo<T>(value);  
        }
        catch
        {
            //Could not convert.  Pass back default value...
            result = default(T);
        }
    }

    return result;
}

0

私はこのクラス設定のようなクラスから始めるのが好きです{publicint X {get; set;} public string Y {get; セットする; } //必要に応じて繰り返す

 public settings()
 {
    this.X = defaultForX;
    this.Y = defaultForY;
    // repeat ...
 }
 public void Parse(Uri uri)
 {
    // parse values from query string.
    // if you need to distinguish from default vs. specified, add an appropriate property

 }

これは何百ものプロジェクトでうまく機能しました。他の多くの解析ソリューションの1つを使用して、値を解析できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.