Tạo một phương thức chung trong C #


84

Tôi đang cố gắng kết hợp một loạt các phương pháp tương tự thành một phương pháp chung. Tôi có một số phương thức trả về giá trị của một chuỗi truy vấn hoặc null nếu chuỗi truy vấn đó không tồn tại hoặc không có định dạng chính xác. Điều này sẽ đủ dễ dàng nếu tất cả các kiểu đều có thể null nguyên bản, nhưng tôi phải sử dụng kiểu chung có thể null cho số nguyên và ngày tháng.

Đây là những gì tôi có bây giờ. Tuy nhiên, nó sẽ trả về giá trị 0 nếu giá trị số không hợp lệ và rất tiếc đó là giá trị hợp lệ trong các trường hợp của tôi. có ai giúp tôi ra ngoài không? Cảm ơn!

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;
}

Anh ta đang chuyển một loạt các triển khai, vì vậy hãy gọi chức năng cũ, ghi nhớ kết quả, gọi chức năng mới, ghi nhớ kết quả, so sánh. Bây giờ làm điều đó 100 lần với một loạt các đầu vào ngẫu nhiên, và thì đấy!
Hamish Grubijan

Tôi xin lỗi, tôi vẫn chưa hiểu cách áp dụng trong trường hợp này. Tôi vẫn đang cố gắng để chức năng hoạt động.
Mike Cole

Nhìn vào các câu trả lời, tôi hơi bối rối: người gọi của bạn đang tham số hóa bằng cách sử dụng int hay int? như T?

Đối với tôi, có vẻ như thay vì xử lý điều này bên trong, bạn nên cho phép phương thức ném ngoại lệ. Có thể đó chỉ là tôi, nhưng ai đó có thể nhầm lẫn tại sao cuộc gọi của họ luôn trả về giá trị mặc định vì họ không thấy ngoại lệ được tạo khi ChangeTypekhông thành công.
nghiền nát

Câu trả lời:


64

Điều gì sẽ xảy ra nếu bạn chỉ định giá trị mặc định để trả về, thay vì sử dụng mặc định (T)?

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

Nó cũng giúp việc gọi nó dễ dàng hơn:

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

Nhược điểm là bạn cần các giá trị ma thuật để biểu thị các giá trị chuỗi truy vấn không hợp lệ / bị thiếu.


Có, điều này có vẻ khả thi hơn là dựa vào giá trị mặc định của một số nguyên. Tôi sẽ ghi nhớ điều này. Tôi vẫn hy vọng hàm ban đầu của mình hoạt động cho tất cả các loại, mặc dù tôi chỉ có thể sử dụng các hàm không chung chung.
Mike Cole

Tại sao không chỉ trả về một số khác 0 cho một số nguyên không hợp lệ? Bạn có thể trả về bất kỳ thứ gì bạn muốn không phải là giá trị hợp lệ hoặc đã có mục đích đặc biệt, chẳng hạn như null. Bạn thậm chí có thể tạo ra loại của riêng bạn có tên là InvalidInteger hoặc một cái gì đó. Bạn đang trả về null cho một chuỗi truy vấn không hợp lệ, phải không? Bạn cũng có thể trả về điều đó cho một số nguyên không hợp lệ, vì vậy null có nghĩa là đơn giản là 'có gì đó tồi tệ và tôi không có giá trị nào đối với bạn' và có thể chuyển Mã lý do bằng cách tham chiếu đến hàm?
Dan Csharpster

1
Làm thế nào để nhận giá trị cho: long ? testnơi mặc định phải là null
Arshad

16

Tôi biết, tôi biết, nhưng ...

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

4
Các Try-pattern nên nổi tiếng đối với bất kỳ nhà phát triển NET. Nó không phải là một điều xấu nếu bạn hỏi tôi. Trong F # hoặc NET 4.0 bạn sẽ sử dụng Option (hoặc Choice)
Christian Klauser

6
Nếu không có lý do nào khác, tôi cố gắng tránh nó bởi vì tôi ghét phải "khai báo trước" biến đầu ra đó, đặc biệt là nếu nó thậm chí không bao giờ được sử dụng - lãng phí những gì có thể là một dòng mã hoàn toàn tốt.
Jay

Trên thực tế, đó là cách đơn giản nhất để giải quyết vấn đề của bạn - xác định một hàm như trên + hai trợ giúp sẽ sử dụng hàm này (đó sẽ là 4 lớp lót).
greenoldman,

1
Tôi ghét mô hình Thử vì lý do giống như Jay đã nói. Tôi muốn một hàm chung nếu có thể, đó là mục tiêu ban đầu của tôi.
Mike Cole

11
Làm hay không không có thử! <Yoda>
Rabbit

12

Cái này thì sao? Thay đổi loại trả lại từ TthànhNullable<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;
        }

Lỗi: Kiểu 'T' phải là kiểu giá trị không thể nullable để sử dụng nó làm tham số 'T' trong kiểu hoặc phương thức chung 'System.Nullable <T>'.
Mike Cole

Bạn cũng cần chỉ định where T : struct.
Aaronaught

@Mike C: Bạn sẽ không gặp lỗi tương tự. Mã đã chỉnh sửa chắc chắn biên dịch.
Aaronaught

Yup, có ngay bây giờ. Vì vậy, điều gì sẽ xảy ra khi tôi muốn gọi điều này cho kiểu Chuỗi? Nó sẽ không chấp nhận nó như bây giờ.
Mike Cole

@MikeC, không nghĩ rằng có thể vì stringlà một nullablegiá trị
Graviton

5

Bạn có thể sử dụng loại Có thể đơn nguyên (mặc dù tôi thích câu trả lời của Jay hơn)

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;
    }
}

Phương pháp của bạn sẽ giống như sau:

    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()không xử lý chính xác các kiểu hoặc kiểu liệt kê nullable trong .NET 2.0 BCL (tôi nghĩ rằng nó đã được sửa cho BCL 4.0 mặc dù). Thay vì làm cho việc triển khai bên ngoài phức tạp hơn, hãy làm cho bộ chuyển đổi làm được nhiều việc hơn cho bạn. Đây là cách triển khai tôi sử dụng:

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);
  }
}

Sau đó, việc triển khai GetQueryString <T> của bạn có thể là:

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

Tôi muốn bắt đầu với một lớp như thế này cài đặt lớp {public int X {get; set;} public string Y {get; bộ; } // lặp lại nếu cần

 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

 }

Điều này đã hoạt động tốt trên 100 dự án. Bạn có thể sử dụng một trong nhiều giải pháp phân tích cú pháp khác để phân tích cú pháp các giá trị.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.