2009-07-09 30 views
17

Tôi có các tình huống sau, nơi tôi muốn vượt qua trong chuỗi và một kiểu generic:Làm cách nào để chuyển đổi thành loại cụ thể trong phiên bản chung của TryParse()?

public class Worker { 
    public void DoSomeWork<T>(string value) 
     where T : struct, IComparable<T>, IEquatable<T> { ... } 
} 

Tại một số điểm dọc theo con đường tôi cần phải chuyển đổi các giá trị chuỗi giá trị T của nó. Nhưng tôi không muốn làm một chuyển đổi thẳng như tôi cần phải thực hiện một số logic nếu chuỗi không thể được chuyển đổi thành loại T.

Tôi đã nghĩ rằng tôi có thể thử sử dụng Convert.ChangeType() nhưng điều này có vấn đề nếu nó không chuyển đổi nó sẽ ném một ngoại lệ và tôi sẽ chạy phương pháp DoSomeWork() thường đủ để không phải dựa vào một thử/bắt để xác định xem chuyển đổi có hợp lệ hay không.

Vì vậy, đây tôi đã suy nghĩ, tôi biết rằng tôi sẽ làm việc với các loại số, do đó T sẽ được bất kỳ những điều sau đây: int, uint, short, ushort, long, ulong, byte, sbyte, decimal, float, double. Biết được điều này tôi nghĩ rằng có thể có một giải pháp nhanh hơn để làm việc với thực tế là tôi biết tôi sẽ sử dụng các loại số (lưu ý nếu T không phải là loại số tôi ném ngoại lệ) ...

public class NumericWorker { 
    public void DoSomeWork<T>(string value) 
     where T : struct, IComparable<T>, IEquatable<T> 
    { 
     ParseDelegate<T> tryConverter = 
      SafeConvert.RetreiveNumericTryParseDelegate<T>(); 
     ... 
    } 
} 


public class SafeConvert 
{ 
    public delegate bool ParseDelegate<T>(string value, out T result); 

    public static ParseDelegate<T> RetreiveNumericTryParseDelegate<T>() 
     where T : struct, IComparable<T>, IEquatable<T> 
    { 
     ParseDelegate<T> tryParseDelegate = null; 

     if (typeof(T) == typeof(int)) 
     { 
      tryParseDelegate = (string v, out T t) => 
       { 
       int typedValue; 
       bool result = int.TryParse(v, out typedValue); 
       t = result ? (T)typedValue : default(T); 
       //(T)Convert.ChangeType(typedValue, typeof(T)) : default(T); 
       return result; 
       }; 
     } 
     else if (typeof(T) == typeof(uint)) { ... } 
     else if (typeof(T) == typeof(short)) { ... } 
     else if (typeof(T) == typeof(ushort)) { ... } 
     else if (typeof(T) == typeof(long)) { ... } 
     else if (typeof(T) == typeof(ulong)) { ... } 
     else if (typeof(T) == typeof(byte)) { ... } 
     else if (typeof(T) == typeof(sbyte)) { ... } 
     else if (typeof(T) == typeof(decimal)) { ... } 
     else if (typeof(T) == typeof(float)) { ... } 
     else if (typeof(T) == typeof(double)) { ... } 

     return tryParseDelegate; 
    } 
} 

Nhưng ở trên có vấn đề là tôi không thể viết t = result ? (T)typedValue : default(T); khi việc tạo typedValue đến T gây ra sự cố và cách duy nhất tôi có thể thực hiện được là viết (T)Convert.ChangeType(typedValue, typeof(T)). Nhưng nếu tôi làm điều này tôi chỉ đang thực hiện một chuyển đổi khác. Vì vậy, tôi đã tự hỏi nếu có ai biết làm thế nào tôi có thể khắc phục vấn đề này (nếu bạn nghĩ rằng làm ChangeType() là một vấn đề) hoặc nếu có một giải pháp tốt hơn hoàn toàn mà tôi đã không xem xét.

Trả lời

37

t = result? (T) typedValue: mặc định (T);

Hãy thử:

t = result ? (T)(object)typedValue : default(T); 

Vâng, Generics có thể được kinda khó chịu vào những thời điểm.

FWIW, tôi sử dụng a much simpler wrapper xung quanh Convert.ChangeType() chỉ thực hiện kiểm tra trước các chuỗi trống. Trừ khi bạn đang sử dụng điều này cho đầu vào người dùng chưa được kiểm tra, điều đó có thể sẽ đủ.

+0

gì tôi đang sử dụng nó cho là xác nhận đầu vào người dùng ... :(do đó, thats lý do tại sao tôi đã không muốn sử dụng changetype và try/catch vì ai biết những gì người dùng sẽ gõ ... –

+0

Heh, ok. :-) FWIW, tôi sẽ không lo lắng quá nhiều về khía cạnh hiệu quả của việc bắt ngoại lệ do 'ChangeType()' đưa ra, nhưng nếu bạn lo lắng về phong cách thì giải pháp của bạn có vẻ ổn. Lưu ý rằng bạn có thể chỉ đơn giản là API bằng cách đun sôi nó xuống một phương thức chung duy nhất, trong đó T được xác định ngầm bởi tham số 'out'. – Shog9

+0

Tôi là tất cả vì sự đơn giản, ý bạn là gì bởi "nơi T được chỉ định ngầm bởi tham số ngoài" ?? –

12

Với này:

do đó T sẽ được bất kỳ những điều sau đây: int, uint, ngắn, ushort, dài, ulong, byte, SByte, số thập phân, float, double.

Tôi khuyên bạn chỉ nên sử dụng Convert.ChangeType và không phải lo lắng về điều đó. Thời gian duy nhất bạn sẽ nhận được một ngoại lệ là khi chuỗi của bạn bị định dạng sai, trong trường hợp đó, bạn có thể trả về mặc định (T).

ví dụ:

try 
{ 
    result = Convert.ChangeType(value, typeof(T)); 
} 
catch 
{ 
    result = default(T); 
} 
+0

Như đã đề cập trong các bình luận tôi đã cho @ Shog9, vấn đề là bởi vì tôi đang sử dụng điều này để xác nhận đầu vào người dùng Tôi lo lắng về chi phí của việc tạo ra các ngoại lệ. afaik tạo ra một ngoại lệ mà bạn biết có thể xảy ra mỗi lần thứ hai phương pháp chạy là một hoạt động tốn kém, do đó tại sao tôi đã cố gắng sử dụng các đại biểu thay thế. Bạn có nói rằng nó vẫn còn giá trị đi với phiên bản bạn đề nghị của phương pháp tiếp cận đại biểu tôi đang cố gắng sử dụng? –

+0

Cá nhân, tôi vẫn sử dụng điều này. Chi phí của các ngoại lệ không phải là điều tôi lo lắng trong trường hợp này, đặc biệt là, nếu nó là đầu vào của người dùng, bạn sẽ không cần gọi điều này trong một vòng lặp chặt chẽ (vì người dùng không thể gõ nhanh như bạn có thể xử lý ngoại lệ). Bạn sẽ có chi phí trong báo cáo chuyển đổi của bạn, các cuộc gọi đại biểu, vv, ngay cả với cách tiếp cận khác, và nó là khủng khiếp để duy trì so với 6 dòng tôi có ở trên. –

+0

Tôi đồng ý với Reed. Ngoại lệ có thể lấy thứ gì đó của hàng chục mili giây ở mức tồi tệ nhất (lạnh); nhưng nếu bạn đang xác thực chuỗi đầu vào của người dùng _one_ thì điều đó hầu như không đáng chú ý. –

5

ToType là tham số chung ở đây.Điều này làm việc cho các loại nullable, chỉ trong trường hợp bạn cần nó. Bạn có thể trích xuất phương thức chính của mình thành trình biến đổi chung, sẽ chuyển đổi thành bất kỳ loại nào, bao gồm cả các giá trị rỗng.

ToType result = default(ToType);  

    result = ChangeType<ToType>(typedValue); 


    private T ChangeType<T>(object o) 
{ 
    Type conversionType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); 
    return (T)Convert.ChangeType(o, conversionType); 
} 
+0

Nếu tôi sử dụng cách tiếp cận này làm thế nào để điều này có được xung quanh sự cần thiết cho một khối try/catch ?? –

+0

ngoại lệ sẽ không bị ném vì bạn đang sử dụng TryParse. nếu kết quả của TryParse của bạn là đúng thì chuyển đổi sẽ hoạt động trừ khi bạn chuyển vào một tham số chung hoàn toàn khác. là tình trạng tiến thoái lưỡng nan của bạn? –

+0

xin lỗi bạn không thể vượt qua một thông số chung khác vì bạn có câu lệnh if trên typeof (T) .. vậy câu hỏi của bạn chính xác là gì? –

1

Tại sao không chỉ sử dụng phản chiếu và sử dụng các phương pháp TryParse tích hợp? Khá nhiều cho mỗi kiểu bản địa ngoại trừ Guid.

public static Parser<T> GetParser<T>(T defaultResult) 
    where T : struct 
{ 
    // create parsing method 
    Parser<T> parser = (string value, out T result) => 
    { 
     // look for TryParse(string value,out T result) 
     var parseMethod = 
      typeof(T).GetMethods() 
        .Where(p => p.Name == "TryParse") 
        .Where(p => p.GetParameters().Length == 2) 
        .Single(); 

     // make parameters, leaving second element uninitialized means out/ref parameter 
     object[] parameters = new object[2]; 
     parameters[0] = value; 

     // run parse method 
     bool success = (bool)parseMethod.Invoke(null, parameters); 

     // if successful, set result to output 
     if (!success) 
     { 
      result = (T)parameters[1]; 
     } 
     else 
     { 
      result = defaultResult; 
     } 

     return success; 
    }; 

    return parser; 
} 
+0

Tôi đang cố gắng tránh phản ánh do hiệu suất –

+0

nếu bạn thử nghiệm nó, bạn có thể thực hiện trên 100.000 phân tích trong 11 giây. tôi so sánh với .net 4 bằng cách sử dụng một cây biểu hiện bản địa mà mất 14 giây (mặc dù có thể không phản ánh perf khi .net 4 được phát hành) –

2

Bạn có thể thử một cái gì đó đơn giản

public static T ConvertValue<T,U>(U value) where U : IConvertible { 
     return (T)ConvertValue(value, typeof(T)); 
    } 

    public static object ConvertValue(IConvertible value, Type targetType) { 
     return Convert.ChangeType(value, targetType); 
    } 
Các vấn đề liên quan