2010-06-06 25 views
5

Theo sau từ câu hỏi của tôi here, tôi đang cố gắng tạo một trình so sánh giá trị chung chung. Tôi đã không bao giờ chơi với sự phản ánh trước vì vậy không chắc chắn nếu tôi đang đi đúng hướng, nhưng dù sao tôi đã có ý tưởng này cho đến nay:Tự động đặt đối số kiểu chung

bool ContainSameValues<T>(T t1, T t2) 
{ 
    if (t1 is ValueType || t1 is string) 
    { 
     return t1.Equals(t2); 
    } 

    else 
    { 
     IEnumerable<PropertyInfo> properties = t1.GetType().GetProperties().Where(p => p.CanRead); 
     foreach (var property in properties) 
     { 
      var p1 = property.GetValue(t1, null); 
      var p2 = property.GetValue(t2, null); 

      if(!ContainSameValues<p1.GetType()>(p1, p2)) 
       return false; 
     } 
    } 
    return true; 
} 

Điều này không biên dịch vì tôi không thể làm việc ra cách đặt loại T trong cuộc gọi đệ quy. Có thể làm điều này một cách năng động không?

Có một số câu hỏi liên quan ở đây mà tôi đã đọc nhưng tôi không thể theo dõi chúng đủ để tìm hiểu cách chúng có thể áp dụng trong trường hợp của tôi.

+0

Không được (t1 là ValueType | t1 là chuỗi)? Với || nếu điều kiện đầu tiên bị lỗi thì điều kiện thứ hai không được kiểm tra. System.String là một kiểu tham chiếu, không phải là một kiểu giá trị. –

Trả lời

6

Bạn có thể tránh sự phản ánh trên lời gọi nếu bạn thấy vui khi so sánh dựa trên các loại thuộc tính tĩnh.

Điều này dựa vào biểu thức trong 3.5 để thực hiện phản xạ một cách đơn giản, có thể thực hiện điều này tốt hơn để giảm nỗ lực cho các loại lồng nhau nhưng điều này sẽ tốt cho hầu hết các nhu cầu.

Nếu bạn phải làm việc với các kiểu thời gian chạy thì cần phải có một số mức phản chiếu (mặc dù điều này sẽ rẻ nếu bạn nhớ lại truy cập thuộc tính và phương thức so sánh) nhưng điều này vốn phức tạp hơn nhiều. tính có thể không phù hợp như vậy, cho tổng quát đầy đủ, bạn sẽ phải xem xét quy tắc như sau:

  • xem xét các loại không phù hợp để không bằng
    • đơn giản để hiểu và dễ thực hiện
    • không có khả năng được một hoạt động hữu ích
  • Tại điểm các loại bất đồng sử dụng các tiêu chuẩn EqualityComparer<T>.Default thực hiện trên hai và recurse không có thêm
    • lại đơn giản, hơi khó để thực hiện.
  • xem xét bình đẳng nếu họ có một tập hợp con chung của các thuộc tính mà bản thân họ bằng
    • phức tạp, không thực sự khủng khiếp có ý nghĩa
  • xem xét bình đẳng nếu họ chia sẻ cùng một tập các thuộc tính (dựa trên tên và loại) mà chính họ là bằng nhau
    • phức tạp, hãy nhập vào đánh máy vịt

Có rất nhiều tùy chọn khác nhưng điều này phải là thực phẩm để suy nghĩ là lý do tại sao phân tích toàn thời gian là khó.

(lưu ý rằng tôi đã thay đổi bạn 'lá' bảo vệ chấm dứt được những gì tôi cho là vượt trội, nếu bạn muốn chỉ cần sử dụng loại sting/giá trị đối với một số lý do cảm thấy tự do)

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Reflection; 
using System.Linq.Expressions; 


class StaticPropertyTypeRecursiveEquality<T> 
{ 
    private static readonly Func<T,T, bool> actualEquals; 

    static StaticPropertyTypeRecursiveEquality() 
    { 
     if (typeof(IEquatable<T>).IsAssignableFrom(typeof(T)) || 
      typeof(T).IsValueType || 
      typeof(T).Equals(typeof(object))) 
     { 
      actualEquals = 
       (t1,t2) => EqualityComparer<T>.Default.Equals(t1, t2); 
     } 
     else 
     { 
      List<Func<T,T,bool>> recursionList = new List<Func<T,T,bool>>(); 
      var getterGeneric = 
       typeof(StaticPropertyTypeRecursiveEquality<T>) 
        .GetMethod("MakePropertyGetter", 
         BindingFlags.NonPublic | BindingFlags.Static); 
      IEnumerable<PropertyInfo> properties = typeof(T) 
       .GetProperties() 
       .Where(p => p.CanRead); 
      foreach (var property in properties)     
      { 
       var specific = getterGeneric 
        .MakeGenericMethod(property.PropertyType); 
       var parameter = Expression.Parameter(typeof(T), "t"); 
       var getterExpression = Expression.Lambda(
        Expression.MakeMemberAccess(parameter, property), 
        parameter); 
       recursionList.Add((Func<T,T,bool>)specific.Invoke(
        null, 
        new object[] { getterExpression }));      
      } 
      actualEquals = (t1,t2) => 
       { 
        foreach (var p in recursionList) 
        { 
         if (t1 == null && t2 == null) 
          return true; 
         if (t1 == null || t2 == null) 
          return false; 
         if (!p(t1,t2)) 
          return false;        
        } 
        return true; 
       }; 
     } 
    } 

    private static Func<T,T,bool> MakePropertyGetter<TProperty>(
     Expression<Func<T,TProperty>> getValueExpression) 
    { 
     var getValue = getValueExpression.Compile(); 
     return (t1,t2) => 
      { 
       return StaticPropertyTypeRecursiveEquality<TProperty> 
        .Equals(getValue(t1), getValue(t2)); 
      }; 
    } 

    public static bool Equals(T t1, T t2) 
    { 
     return actualEquals(t1,t2); 
    } 
} 

để thử nghiệm Tôi đã sử dụng các thông tin sau:

public class Foo 
{ 
    public int A { get; set; } 
    public int B { get; set; } 
} 

public class Loop 
{ 
    public int A { get; set; } 
    public Loop B { get; set; } 
} 

public class Test 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine(StaticPropertyTypeRecursiveEquality<String>.Equals(
      "foo", "bar")); 
     Console.WriteLine(StaticPropertyTypeRecursiveEquality<Foo>.Equals(
      new Foo() { A = 1, B = 2 }, 
      new Foo() { A = 1, B = 2 })); 
     Console.WriteLine(StaticPropertyTypeRecursiveEquality<Loop>.Equals(
      new Loop() { A = 1, B = new Loop() { A = 3 } }, 
      new Loop() { A = 1, B = new Loop() { A = 3 } })); 
     Console.ReadLine(); 
    } 
} 
+0

cảm ơn câu trả lời rất chi tiết và mang tính thông tin. – fearofawhackplanet

3

Bạn cần phải gọi phương thức sử dụng phản chiếu, như thế này:

MethodInfo genericMethod = typeof(SomeClass).GetMethod("ContainSameValues"); 
MethodInfo specificMethod = genericMethod.MakeGenericMethod(p1.GetType()); 
if (!(bool)specificMethod.Invoke(this, new object[] { p1, p2 })) 

Tuy nhiên, phương pháp của bạn không nên chung chung ở nơi đầu tiên; nó chỉ cần lấy hai tham số object. (Hoặc, nếu nó là chung chung, nó nên cache thuộc tính và các đại biểu trong một loại chung)

+0

Btw bạn có thể cải thiện hiệu suất sử dụng Expression thay vì Reflection. –

+0

@Danny: Nhưng chỉ khi bạn cache biểu thức. (Trong một trường tĩnh trong một kiểu chung) Ngoài ra, bạn có thể có được hiệu năng tốt hơn bằng cách tạo một đại biểu. – SLaks

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