2012-05-13 31 views
17

trong một trong các dự án của tôi, tôi đang sử dụng hai phương pháp sau đây. 1. GetDoubleValue và 2. GetIntValue. GetDoubleValue sử dụng double.TryParse để tham số str chuỗi và trả về 0 nếu nó thất bại trong khi GetIntValue cố gắng int.TryParse tham số str chuỗi và trả về 0 nếu nó không thành công. Những gì tôi muốn là kết hợp hai phương thức này thành một phương thức chung mà cùng với chuỗi str nhận tham số T cũng như vậy nếu tôi muốn sử dụng phương thức GetDoubleValue tôi có thể sử dụng gấp đôi tham số T và nếu tôi muốn sử dụng phương thức GetIntValue tôi có thể sử dụng Int cho tham số Tcách sử dụng <T> .TryParse trong một Phương thức chung trong khi T là hai hoặc Int

public double GetDoubleValue(string str) 
{ 
    double d; 
    double.TryParse(str, out d); 
    return d; 
} 
public int GetIntValue(string str) 
{ 
    int i; 
    int.TryParse(str, out i); 
    return i; 
} 

Lưu ý: Tôi đã thử một cái gì đó như thế này;

private T GetDoubleOrIntValue<T>(string str) where T : struct 
{ 
    T t; 
    t.TryParse(str, out t); 
    return t; 
} 

EDIT

Trong cơ sở dữ liệu của tôi, tôi có hơn 30 cột trong bảng differenct có datatype số. Tôi muốn chèn 0 vào mỗi cột nếu người dùng không gõ bất cứ điều gì trong hộp văn bản, tức là anh ta để lại tất cả hoặc một số hộp văn bản trống. Nếu tôi không sử dụng phương thức GetIntValue, tôi sẽ phải sử dụng thân phương thức hơn 30 lần. đó là lý do tại sao tôi làm điều này thông qua phương pháp tiếp cận phương pháp. Tôi đang viết ba của hơn ba mươi ví dụ ví dụ

cmd.Parameters.Add("@AddmissionFee", SqlDbType.Decimal).Value = GetIntValue(tbadmissionfee.Text); 
cmd.Parameters.Add("@ComputerFee", SqlDbType.Decimal).Value = GetIntValue(tbcomputerfee.Text); 
cmd.Parameters.Add("@NotesCharges", SqlDbType.Decimal).Value = GetDoubleValue(tbnotescharges.Text); 

Tôi muốn kết hợp hai phương pháp nêu trên vì hôm nay tôi đang gặp hai phương pháp như thế này mà nếu kết hợp sẽ không đưa ra bất cứ sự cải thiện tốt hơn trong lập trình nhưng ngày mai tôi có thể có hàng chục phương pháp như thế này sẽ tốt hơn được kết hợp thành một phương pháp chung chung. ví dụ tôi có thể có GetInt32Value, GetShortValue vv hy vọng nó bây giờ là xóa lý do tại sao tôi muốn điều này ???

+2

Với cây trồng hiện tại của những hạn chế kiểu chung chung, điều này là không thể. – Oded

+0

Bạn sẽ gọi phương thức chung này là gì? – BoltClock

+2

Bạn có chắc chắn muốn hàm này tự động trả về giá trị mặc định nếu phân tích cú pháp không thành công? – Lee

Trả lời

5

Bạn có thể làm một cái gì đó như thế này:

public static T GetDoubleOrIntValue<T>(this string str) where T : IConvertible 
{ 
    var thisType = default(T); 
    var typeCode = thisType.GetTypeCode(); 
    if (typeCode == TypeCode.Double) 
    { 
     double d; 
     double.TryParse(str, out d); 
     return (T)Convert.ChangeType(d,typeCode) ; 
    } 
    else if (typeCode == TypeCode.Int32) 
    { 
     int i; 
     int.TryParse(str, out i); 
     return (T)Convert.ChangeType(i, typeCode); 
    } 
    return thisType; 
} 

Sau đó, khi bạn gọi nó là:

string d = "1.1"; 
string i = "3"; 

double doubleValue = d.GetDoubleOrIntValue<double>(); 
int intValue = i.GetDoubleOrIntValue<int>(); 

Nhưng toàn bộ điều dường như kinda ngớ ngẩn với tôi.

EDIT: Thấy người khác sử dụng Convert.ChangeType ... cung cấp loại trả về chung.

+0

Cảm ơn Eric Dahlvang. mã bạn cung cấp đủ lớn để thay thế hai phương pháp trên của tôi theo đề xuất của Mark Byers – kashif

+0

Tôi đã suy nghĩ về mã hóa của bạn ngày hôm nay. nó thực sự không phải là giải pháp tồi cho vấn đề của tôi. cảm ơn rất nhiều. – kashif

+0

tôi phải nói điều này thực sự hút, nhiều như tôi yêu C# tôi muốn có một convert.try (loại int, giá trị đối tượng) – CMS

1

Bạn có thể thử với sự phản chiếu: đầu tiên, hãy thử tìm phương thức "TryParse" trên loại được thay thế bằng <T>.

T t; 
var typeInfo = typeof(T); 
var method = typeInfo.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static); 
var result = (bool)method.Invoke(null, new object[] { str, t }); 

if (result) 
    return t; 

Mã này không được kiểm tra; không phải là nó tối ưu hiệu suất-khôn ngoan.

+2

Điều này sẽ không hoạt động. Phương thức 'TryParse' sửa đổi cá thể mà bạn truyền sang nó, không phải là' t', mà là một bản sao của nó. – svick

+0

Ngoài ra, một vài vấn đề khác (mặc dù không lớn): bạn cần phải khởi tạo 't' trước khi bạn có thể sử dụng nó; bạn cần phải xác định 'TryParse' nào bạn muốn (' int' có hai trong số chúng); bạn phải làm điều gì đó khi 'kết quả' là' sai'. – svick

6

Vì vậy, thay vì viết:

double d = GetDoubleValue(str); 

Bạn muốn để có thể viết những dòng này?

double d = GetValue<double>(str); 

Thậm chí nếu bạn có thể làm cho nó hoạt động, lợi ích là gì? Cá nhân tôi không nghĩ đó là một cải tiến lớn cho khách hàng. Ưu điểm duy nhất là không phải thực hiện cùng một phương pháp hai lần. Nhưng với sự đơn giản của phương pháp và khó khăn trong việc triển khai loại mã này, có vẻ như hợp lý ở đây chỉ để lặp lại vài dòng mã đó.

Bạn không đơn độc với vấn đề này. Hãy xem các phương thức khác trong khung công tác .NET hoạt động trên các kiểu khác nhau và xem cách chúng giải quyết nó như thế nào. Đây là cách BinaryReader cho phép bạn đọc các loại khác nhau:

Nó không đẹp, nhưng đó là cách nó thường được thực hiện.


Về bản cập nhật của bạn, tôi có thêm hai điểm để thực hiện.

Bạn đề cập rằng bạn có thể sẽ có nhiều hơn hai loại và điều này có thể dẫn đến nhiều bản sao của cùng một mã. Cho rằng phương pháp này rất súc tích và đơn giản, có vẻ như không sao chép và dán mã. Đó là một ngoại lệ hợp lý đối với quy tắc mà bạn không nên sao chép và dán mã, theo ý kiến ​​của tôi. Cách tiếp cận here làm giảm số lượng trùng lặp, mặc dù bạn vẫn cần nhiều phương thức được đặt tên tương tự trong giao diện công khai của mình.

Tuy nhiên, tôi nghĩ rằng điều quan trọng là phải đề cập đến việc âm thầm sử dụng giá trị mặc định trong trường hợp của bạn, như Lee đã đề cập trong một nhận xét. Nếu người dùng mắc lỗi trong khi nhập dữ liệu, họ sẽ nhận được thông báo lỗi. Bạn nên sử dụng một số loại khung xác nhận để đảm bảo rằng các chuỗi hợp lệ và thông báo cho người dùng của bạn biết vấn đề là gì nếu chúng không hợp lệ. Khi bạn đã xác thực mọi thứ, an toàn để sử dụng int.Parse thay vì int.TryParse vì bạn biết rằng việc phân tích cú pháp sẽ thành công. Và nếu phân tích cú pháp thất bại, đó là một tình huống đặc biệt (một lỗi trong quá trình xác nhận của bạn) để có vẻ công bằng khi ứng dụng gửi ra với một ngoại lệ và ghi lại một dấu vết ngăn xếp. Điều này sẽ giúp bạn tìm và sửa lỗi.

+0

@Marks Byers Cảm ơn. Bạn đã cho tôi 100% quyền. Hmm vì vậy không có cách nào để làm điều đó ?????? – kashif

+0

@ kashif: Thật khó để biết giải pháp "đúng" là gì bởi vì bạn chưa nói lý do tại sao bạn cần điều này. Tại sao bạn không sử dụng TryParse trực tiếp thay vì ẩn nó đằng sau một cuộc gọi phương thức? Các nhà thiết kế .NET suy nghĩ kỹ về API cho TryParse và đó là những gì họ nghĩ ra. Về cơ bản, bạn chỉ thay đổi API để bạn trả về 0 thay vì sai khi phân tích cú pháp không thành công. Làm thế nào là tốt hơn? Nếu bạn thực sự muốn điều này, làm thế nào về việc cho phép khách hàng để chọn giá trị mặc định? Ví dụ: 'TryParse (chuỗi s, int defaultValue)' và 'TryParse (chuỗi s, double defaultValue)'? –

2

Tôi đã tạo một mẫu đơn giản. Mã không phải là tối ưu, nhưng nó hoạt động như mong đợi.

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine(GetValue<int>("123")); 
     Console.WriteLine(GetValue<double>("123.123")); 
     Console.WriteLine(GetValue<DateTime>("2001-01-01 01:01:01")); 
    } 

    static T GetValue<T>(string s) 
    { 
     var tryParse = typeof (T).GetMethod(
      "TryParse", new [] {typeof(string), typeof(T).MakeByRefType()}); 
     if (tryParse == null) 
      throw new InvalidOperationException(); 


     T t = default (T); 
     var parameters = new object[] {s, t}; 
     var success = tryParse.Invoke(null, parameters); 
     if ((bool) success) t = (T)parameters[1]; 
     return t; 
    } 
} 
+0

Thay vì chỉ dựa vào số lượng tham số, tôi thà sử dụng quá tải 'GetMethod()' cho phép bạn chỉ định các tham số. Nếu bạn làm điều đó, đừng quên sử dụng ['MakeByRefType()'] (http://msdn.microsoft.com/en-us/library/system.type.makebyreftype.aspx). – svick

+0

@svick right! mã được cập nhật. –

+0

điều này đang hoạt động chính xác theo cách tôi muốn nhưng tôi thà sử dụng hai phương pháp trên thay vì sử dụng mã cực lớn này – kashif

0

Như bạn thấy bạn không thể hạn chế where T để Double hoặc Int32, đó là lý do tại sao bạn có thể có struct trên rằng nơi (int và double không struct). Không thể sử dụng các loại giá trị làm hạn chế về generics.

Vì vậy, bạn không thể nhập an toàn đảm bảo rằng phương pháp TryParse tồn tại. Bạn nên đánh máy T và ném một ngoại lệ nếu nó là double hoặc int như trong câu trả lời bởi @ie.

Đề nghị của tôi là sử dụng chữ ký khác nhau:

private static bool GetValue(string str, out double result) 
{ 
...  
} 

private static bool GetValue(string str, out int result) 
{ 
...  
} 

Hoặc bạn có thể giới thiệu một giao diện IParsable. Hộp double hoặc int kết quả vào đó và triển khai chuyển đổi ngầm thành hai hoặc số nguyên.

+0

'int' và' double' là 'struct's, ít nhất là cho mục đích của các ràng buộc kiểu generic. – svick

10

Tôi đồng ý với Mark Byers. Nó có thể không phải là một ý tưởng tốt để thử làm cho phương pháp này chung chung. Một chút mã trùng lặp sẽ không bị tổn thương (miễn là nó thực sự chỉ là một chút). Thực tế là bạn có thể sử dụng bất kỳ struct với phiên bản chung của bạn cũng không làm cho nó trông giống như một ý tưởng tốt cho tôi.

Nếu bạn thực sự muốn làm điều này, bạn có thể thử sử dụng sự phản chiếu (như Minustar gợi ý), nhưng điều đó sẽ vừa xấu vừa chậm.

Thay vào đó, bạn có thể sử dụng Convert.ChangeType():

private T GetValue<T>(string str) where T : struct 
{ 
    return (T)Convert.ChangeType(str, typeof(T)); 
} 
+0

Cảm ơn ChangeType – makoshichi

+0

, chúng ta cần thử ChangeType – CMS

7

tôi sẽ xem xét cách viết một phần mở rộng hoặc phương pháp tĩnh:

delegate bool TryParse<T>(string str, out T value); 

public static T GetValue<T>(this string str, TryParse<T> parseFunc) 
{ 
    T val; 
    parseFunc(str, out val); 
    return val; 
} 

Sau đó bạn phải cung cấp việc thực hiện TryParse bạn muốn sử dụng:

int i = "1234".GetValue<int>(int.TryParse); 

Được cảnh báo rằng chức năng này âm thầm urns một giá trị mặc định nếu phân tích cú pháp không thành công. Bạn có thể muốn trả lại default(T) nếu số đại biểu trả về sai số TryParse.

+0

Điều này có vẻ như quá nhiều lần sao chép mã cho tôi. – svick

+0

@svick - Điều này có ít trùng lặp hơn so với việc tạo một hàm cho từng loại bạn muốn phân tích cú pháp. Nó có thể là quá mức cần thiết nếu bạn chỉ cần xử lý 'int' và 'double'. – Lee

+2

+1 Cách tiếp cận này được hiển thị ở đây: http://geekswithblogs.net/michelotti/archive/2008/10/05/tryparse-extension-methods.aspx Tôi đồng ý rằng có thể quá mức cần thiết trong trường hợp này, nhưng là một lớp tiện ích chung nó có thể hữu ích. –

0

Còn phương pháp khuyến nông thì sao?

public static class Extensions 
{  
    public static Nullable<T> TryParse<T>(this String str) where T:struct 
    { 
     try 
     { 
      T parsedValue = (T)Convert.ChangeType(str, typeof(T)); 
      return parsedValue; 
     } 
     catch { return null; } 
    } 
} 

sử dụng:

int i = "123".TryParse<int>() ?? 0; 
Các vấn đề liên quan