2009-05-13 45 views
14

Tôi có một giao diện cho một tài sản đồ còn yếu kém:Đúc giá trị cho T trong một phương pháp chung

interface IPropertyMap 
{ 
    bool Exists(string key); 
    int GetInt(string key); 
    string GetString(string key); 
    //etc.. 
} 

Tôi muốn tạo ra một phương pháp mở rộng như vậy:

public static T GetOrDefault<T>(this IPropertyMap map, string key, T defaultValue) 
{ 
    if (!map.Exists(key)) 
     return defaultValue; 
    else 
    { 
     if (typeof(T) == typeof(int)) return (T)map.GetInt(key); 
     //etc.. 
    } 
} 

Nhưng trình biên dịch won Đừng để tôi truyền sang T. Tôi đã thử thêm where T : struct nhưng điều đó dường như không giúp ích gì.

Tôi đang thiếu gì?

+0

Tôi đoán đó là lỗi đánh máy, nhưng tất cả các phương thức trong giao diện của bạn đều trả về bool ...? –

+0

Yup, sao chép-dán là người bạn nguy hiểm của tôi ... – Benjol

Trả lời

27

Tôi tin rằng điều này là do trình biên dịch không biết loại hoạt động cần thực hiện. IIRC, bạn có thể làm cho nó hoạt động nếu bạn giới thiệu quyền anh:

if (typeof(T) == typeof(int)) return (T)(object)map.GetInt(key); 

nhưng đó không phải là lý tưởng về hiệu suất.

Tôi nghĩ nó chỉ là một hạn chế của generics, thật không may.

+1

Tôi không looping trên này, vì vậy tôi nghi ngờ hiệu suất sẽ là một vấn đề. – Benjol

+0

Tôi vừa làm một thử nghiệm nhỏ của GetOrDefault chống lại GetInt, sự khác biệt là khá ấn tượng, đối với một triệu lần lặp lại, nhưng vẫn không thể bỏ qua cho các nhu cầu hiện tại của tôi. – Benjol

3

Điều gì làm GetInt, GetString vv làm nội bộ? Có thể có những lựa chọn khác liên quan đến Convert.ChangeType(...) hoặc TypeDescriptor.GetConverter(...).ConvertFrom(...), và một dàn diễn viên duy nhất, sử dụng một "đối tượng" indexer:

ví dụ, nếu các đối tượng đã được đánh máy một cách chính xác:

public T GetOrDefault<T>(this IPropertyMap map, string key, T defaultValue) 
{ 
    return map.Exists(key) ? (T)map[key] : defaultValue; 
} 

hoặc nếu chúng được lưu trữ như các chuỗi và cần chuyển đổi, liên quan đến:

T typedVal = (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(map[key]); 
+0

Vâng, tôi không biết vì tôi không có quyền truy cập vào tất cả các triển khai của giao diện. Có bất kỳ sự khác biệt nào khi sử dụng ChangeType (trả về đối tượng) để chỉ đấm bốc như Jon đã đề xuất không? – Benjol

+0

Xem chỉnh sửa - nhưng nếu bạn không kiểm soát giao diện, đó không phải là tùy chọn, tôi mong đợi ... –

3

Tôi cho rằng đó chỉ là lỗi đánh máy, nhưng bool GetInt(string key) có vẻ lạ. Nó phải là int GetInt(string key) hoặc tốt hơn là int GetInt32(string key).

Tiếp theo, Jon đã lưu ý rằng cần phải có boxing để mã của bạn hoạt động, vì vậy đây là những gì bạn làm.

Và cuối cùng, thêm một phương pháp "catch-all" để giao diện IPropertyMap của bạn - nói object GetValue(string key) và sau đó viết lại GetOrDefault<T> để sử dụng phương pháp này thay vì vô tận và lỗi Type so sánh dễ bị:

else 
    return (T)(object)map.GetValue(key);  
+0

Rất tốt, không hoạt động đối với giao diện tôi đã đề cập, nhưng tôi vừa phát hiện ra một giao diện khác * có GetAsObject(). – Benjol

0

Chỉ để tham khảo , tôi phát hiện ra một giao diện mà không có GetType() và phương pháp GetAsObject(), cho phép tôi để tích hợp các yếu tố của những câu trả lời để làm điều này:

public static T GetOrDefault<T>(this IInfosContainer container, string key, T defaultValue) 
{ 
    //I just read p273 of C# in Depth, +1 Jon Skeet :) 
    if (container == null) throw new ArgumentNullException("container"); 
    if (container.Exist(key)) 
    { 
     if (container.GetType(key) != typeof(T)) 
      throw new ArgumentOutOfRangeException("key", 
       "Key exists, but not same type as defaultValue parameter"); 
     else 
      return (T)container.GetAsObject(key); 
    } 
    else 
     return defaultValue; 
} 

(Purists sẽ lưu ý rằng tôi không thuộc trường 'niềng răng cho một tuyên bố' ...)

0

Tôi không nghĩ đây là một phương pháp tốt. Bạn không có cách nào kiểm soát T là gì. Ví dụ:

giá trị float = map.GetOrDefault ("blah", 2.0);

sẽ không biên dịch vì Không thể chuyển đổi hoàn toàn loại 'double' thành 'float'. Một chuyển đổi rõ ràng tồn tại (bạn đang bỏ lỡ một dàn diễn viên?) Điểm lỗi khác là khi giá trị mặc định mong muốn là null.Phương pháp này để lại dev tại lòng thương xót của trình biên dịch để giải quyết những gì nó nghĩ rằng dev dự định.

Nếu bạn có thể thay đổi giao diện, sau đó thêm phương thức GetObject. Vì bạn đang sử dụng một phương thức mở rộng, tôi giả sử bạn không thể bỏ qua một đối tượng sau đó đến int. Dù bằng cách nào thay đổi phương thức để trông giống như

public static void GetOrDefault (bản đồ này IPropertyMap, phím chuỗi, ref T value) { if (map.Exists (key)) { if (typeof (T) = = typeof (int)) { giá trị = (T) (đối tượng) map.GetInt (khóa); } giá trị = mặc định (T); // đây chỉ là một điều tuyệt vời vì tôi lười biếng, // thêm mã thực tại đây. }} và gọi như thế này

 PropertyMap map = new PropertyMap(); 
     float value = 2.0f; 
     map.GetOrDefault("blah", ref value); 

Tôi ghét params ref nhưng tôi nhìn thấy điểm ở đây. Việc đăng ký là một ví dụ cổ điển khi loại phương pháp này hữu ích. Đoạn mã trên buộc người dùng dev phát triển xác định loại đầu ra và giữ nguyên giá trị mặc định của khái niệm.

+0

Yeh, tôi đã phát hiện ra vấn đề null, hiện tại tôi chỉ sử dụng điều này cho các cấu trúc. Tuy nhiên, tôi tin rằng không có gì ngăn cản bạn bao gồm các tham số kiểu rõ ràng để làm việc vòng giới hạn này. map.GetOrDefault ("dibble", null); – Benjol

Các vấn đề liên quan