2011-01-09 34 views
11

Tương tự cho các phương pháp quá:Làm cách nào để so sánh hai PropertyInfos hoặc các phương thức đáng tin cậy?

tôi đưa ra hai trường hợp của PropertyInfo hoặc các phương pháp đã được chiết xuất từ ​​lớp họ ngồi trên qua GetProperty() hoặc GetMember() vv, (hoặc từ một MemberExpression có thể).

Tôi muốn để xác định xem họ đang có trong thực tế đề cập đến các tài sản tương tự hoặc phương pháp tương tự như vậy

(propertyOne == propertyTwo) 

hoặc

(methodOne == methodTwo) 

Rõ ràng rằng sẽ không thực sự làm việc, có lẽ bạn đang xem xét cùng một thuộc tính, nhưng nó có thể đã được trích xuất từ ​​các cấp khác nhau của hệ thống phân cấp lớp (trong trường hợp này thường là, propertyOne != propertyTwo)

Của cours e, tôi có thể nhìn vào DeclaringType, và tái yêu cầu tài sản, nhưng điều này bắt đầu nhận được một chút bối rối khi bạn bắt đầu nghĩ về

  • Thuộc tính/Phương pháp tuyên bố trên giao diện và thực hiện trên lớp
  • Thuộc tính/phương pháp kê khai trên một lớp cơ sở (hầu như) và ghi đè vào các lớp thừa kế
  • Thuộc tính/Phương pháp tuyên bố trên một lớp cơ sở, ghi đè với 'mới' (trong IL thế giới này là gì iirc đặc biệt)

vào cuối của ngày, tôi chỉ muốn có thể làm một eq thông minh kiểm tra tình trạng giữa hai thuộc tính hoặc hai phương pháp, tôi chắc chắn rằng 80% điểm đạn trên không bao gồm tất cả các trường hợp cạnh, và trong khi tôi có thể ngồi xuống, viết một loạt các bài kiểm tra và bắt đầu chơi, tôi ' Tôi biết rằng kiến ​​thức cấp thấp của tôi về cách các khái niệm này thực sự được triển khai không phải là tuyệt vời và tôi hy vọng đây là chủ đề đã được trả lời và tôi chỉ tìm kiếm.

Câu trả lời tốt nhất là cho tôi một vài phương pháp mà đạt được nêu trên, giải thích những gì trường hợp cạnh đã được đưa về chăm sóc và tại sao :-)


Làm rõ:

Theo nghĩa đen, Tôi muốn đảm bảo chúng là cùng một thuộc tính, dưới đây là một số ví dụ:

public interface IFoo 
{ 
    string Bar { get; set; } 
} 

public class Foo : IFoo 
{ 
    string Bar { get; set; } 
} 

typeof(IFoo).GetProperty("Bar") 

typeof(Foo).GetProperty("Bar") 

Sẽ trở lại hai infos tài sản, mà không bằng nhau:

public class BaseClass 
{ 
    public string SomeProperty { get; set ; } 
} 

public class DerivedClass : BaseClass { } 


typeof(BaseClass).GetMethod("SomeProperty") 

typeof(DerivedClass).GetProperty("SomeProperty") 

tôi có thể không thực sự nhớ nếu hai trở lại đối tượng bằng nhau bây giờ, nhưng trong thế giới của tôi Họ đều bình đẳng.

Tương tự:

public class BaseClass 
{ 
    public virtual SomeMethod() { } 
} 

public class DerivedClass 
{ 
    public override SomeMethod() { } 
} 

typeof(BaseClass).GetMethod("SomeMethod") 

typeof(DerivedClass).GetProperty("SomeMethod") 

Một lần nữa, chúng sẽ không phù hợp - nhưng tôi muốn họ (Tôi biết họ không đặc biệt như nhau, nhưng trong phạm vi của tôi họ là vì chúng đề cập đến cùng một thuộc tính gốc)

Tôi có thể làm điều đó một cách có cấu trúc, nhưng điều đó sẽ là 'sai'.

Ghi chú Hơn nữa:

Làm thế nào để bạn thậm chí yêu cầu các tài sản đó là che giấu tài sản khác? Dường như một trong các giả định trước đó của tôi không hợp lệ, việc thực thi mặc định là GetProperty("name") sẽ chuyển sang cấp hiện tại theo mặc định.

BindingFlags.DeclaringType xuất hiện chỉ để kết thúc trả về giá trị rỗng!

+1

"Thuộc tính/phương pháp được khai báo trên lớp cơ sở, ghi đè bằng 'mới'" - Điều đó thực sự được gọi là * ẩn * và chúng * chắc chắn * các thành viên khác nhau. Nó không có ý nghĩa để xem chúng là "bình đẳng" ở tất cả. – Ani

+0

Vâng rõ ràng, tôi chỉ phrased rằng xấu - nó sẽ rất khó để viết mã so sánh để trở lại đúng trong trường hợp đó ;-) - nó vẫn là một con đường không hạnh phúc cần thử nghiệm, như tôi có thể nghĩ về mã so sánh cấu trúc . –

+1

Bạn có thể làm rõ câu hỏi tốt hơn không? Bạn có muốn 'string.GetHashCode == int.GetHashCode' (vì các định nghĩa cơ sở của chúng có cùng kiểu -' đối tượng') không. Làm thế nào về 'Danh sách .Count' so với' HashSet .Count' (cả hai thực hiện 'ICollection .Count')? – Ani

Trả lời

3

Lấy một cái nhìn tại các PropertyInfo đối tượng từ ví dụ IFoo/Foo bạn, chúng tôi có thể đạt được những kết luận:

  1. Không có cách nào trực tiếp để xem những gì lớp/giao diện tài sản được tuyên bố trên ban đầu.
  2. Do đó, để kiểm tra xem thuộc tính có được khai báo trên lớp tổ tiên không, chúng ta cần lặp lại tổ tiên và xem liệu thuộc tính có tồn tại trên tổ tiên hay không.
  3. Cùng một giao diện, chúng ta cần gọi Type.GetInterfaces và làm việc từ đó. Đừng quên rằng các giao diện có thể thực hiện các giao diện khác, do đó, điều này phải được đệ quy.

Vì vậy, hãy có một vết nứt ở đó. Trước hết, để trang trải tài sản thừa kế:

PropertyInfo GetRootProperty(PropertyInfo pi) 
{ 
    var type = pi.DeclaringType; 

    while (true) { 
     type = type.BaseType; 

     if (type == null) { 
      return pi; 
     } 

     var flags = BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance | 
        BindingFlags.Public | BindingFlags.Static; 
     var inheritedProperty = type.GetProperty(pi.Name, flags); 

     if (inheritedProperty == null) { 
      return pi; 
     } 

     pi = inheritedProperty; 
    } 
} 

Bây giờ, để trang trải các thuộc tính khai báo trong giao diện (tìm kiếm với DFS):

PropertyInfo GetImplementedProperty(PropertyInfo pi) 
{ 
    var type = pi.DeclaringType; 
    var interfaces = type.GetInterfaces(); 

    if (interfaces.Length == 0) { 
     return pi; 
    } 

    var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public; 
    var query = from iface in interfaces 
       let implementedProperty = iface.GetProperty(pi.Name, flags) 
       where implementedProperty != pi 
       select implementedProperty; 

    return query.DefaultIfEmpty(pi).First(); 
} 

Ràng buộc này lại với nhau:

PropertyInfo GetSourceProperty(PropertyInfo pi) 
{ 
    var inherited = this.GetRootProperty(pi); 
    if (inherited != pi) { 
     return inherited; 
    } 

    var implemented = this.GetImplementedProperty(pi); 
    if (implemented != pi) { 
     return implemented; 
    } 

    return pi; 
} 

này nên làm việc. Nó không tính đến các thuộc tính được lập chỉ mục có cùng tên nhưng các kiểu khác nhau và/hoặc số tham số lập chỉ mục, do đó, nó được để lại như là bài tập ngôn ngữ cho người đọc.

Tuyên bố từ chối trách nhiệm: Tôi thậm chí không biên dịch (không có thời gian để chạy thử nghiệm ngay bây giờ). Nó được dự định là điểm bắt đầu cho câu trả lời "the", vì không có câu trả lời nào cho đến nay.

+0

Vẫn là một khởi đầu tuyệt vời - tôi có một bộ kiểm tra và một đống nhưng không làm việc nhưng tương tự) mã để kiểm tra nó với - Tôi sẽ đọc bài viết của bạn và xem nơi khác biệt nằm và lấy lại cho bạn :-) –

+0

Bah, cho đến nay rất tốt nhưng có một sự khác biệt giữa Mono và MS.NET vì vậy tôi không thể xác minh đầy đủ (không thể chạy thử nghiệm trên máy ảo Windows của tôi vì lý do nào đó) - Tôi sẽ làm cho nó hoạt động và chấp nhận câu trả lời của bạn khi tôi đã xác minh các trường hợp cạnh –

+0

Được rồi, gần như vậy - FYI Nó không đủ tốt để làm một GetP roperty, chúng ta phải khớp với tài sản bằng cách sử dụng InterfaceMap - khác hơn là kosher cho đến nay –

1

Có vẻ như sẽ đơn giản hơn để kiểm tra các loại khai báo của hai số MemberInfo mà bạn muốn so sánh. Trong trường hợp của một mối quan hệ cơ sở/phân lớp, chúng phải thể hiện cùng một khai báo nếu các kiểu khai báo là giống nhau.Trong trường hợp các giao diện, họ sẽ có cùng nếu các loại giao diện tuyên bố nằm trong danh sách các giao diện của người kia:

Type type1 = methodInfo1.DeclaringType; 
Type type2 = methodInfo2.DeclaringType; 

bool same = type1 == type2 || 
    type1.IsInterface && type2.GetInterfaces.Contains(type1) || 
    type2.IsInterface && type1.GetInterfaces.Contains(type2); 

Một điều cần phải nhận thức của các giao diện là 'giao diện bản đồ' - Type.GetInterfaceMap có nghĩa rằng các phương thức được khai báo trong một giao diện có thể không có cùng tên trong lớp thực hiện mà phương pháp tiếp cận hiện tại của bạn dường như không tính đến.

+0

Điều này sẽ vẫn trả về false nếu một trong các thành viên một thành viên cơ sở (trong định nghĩa của tôi là giống nhau), và tất nhiên sẽ trả về true nếu hai loại hoàn toàn khác nhau nhưng có cùng loại - thừa nhận đây chỉ là lỗi đánh máy vì bạn vừa mới đưa câu trả lời này vào câu trả lời: Mặc dù vậy, điều này rất đơn giản thực sự là –

+1

Dường như với tôi rằng giải pháp dài nhất là tốt nhất - tìm gốc của BOTH thành viên, và so sánh chúng, dựa vào việc so sánh memberOne/memberTwo trực tiếp ở tất cả dường như bị lỗi ở mức tốt nhất –

2

Tôi không chắc chắn những gì bạn cần cho nhưng tôi đoán định nghĩa bình đẳng của bạn trong trường hợp này là "làm hai phương thức infos gọi cùng một phương thức nếu được gọi"? Nếu vậy thì bạn thực sự cần phải xác định một loại trong sự so sánh quá, ví dụ xem xét việc này:

public interface IFoo 
{ 
    void AMethod(); 
} 

public interface IBar 
{ 
    void AMethod(); 
} 

public class FooBar : IFoo, IBar 
{ 
    void AMethod(); 
} 

Rõ ràng typeof (IFoo) .GetMethod ("AMethod") và typeof (Ibar) .GetMethod ("AMethod") không bằng nhau, chúng thậm chí không liên quan, NHƯNG chúng thực hiện cùng một phương thức trên một cá thể của FooBar. Vì vậy, những gì bạn có thể muốn là một phương pháp so sánh mà mất ba đối số:

bool WillInvokeSameMethodOnType(MethodInfo method1, MethodInfo method2, Type type) 

Kiểm tra các MethodInfoManager lớp trong FakeItEasy, nó có thể là những gì bạn muốn: http://code.google.com/p/fakeiteasy/source/browse/Source/FakeItEasy/Core/MethodInfoManager.cs?r=8888fefbc508fb02d5435a3e33774500bec498b3

+0

Làm các loại có sẵn sẽ khá khó khăn, nhưng DeclaringType vv chắc chắn sẽ là đủ? Tôi nghĩ rằng MethodInfoManager đến gần - nhưng làm thế nào để bạn quản lý mới vs ảo? –

+0

Không chắc chắn chính xác những gì bạn đang yêu cầu, không cần phải có trường hợp đặc biệt mới so với ảo trong trường hợp của tôi vì tất cả những gì tôi quan tâm là thông báo cho phương thức sẽ gửi cùng một phương thức. –

2

Vì vậy, đây là một cookie khó khăn và trước Tôi đi vào chi tiết nhàm chán Tôi sẽ nói điều này, tôi đã chọn chỉ làm một so sánh cấu trúc, là nơi duy nhất rằng sẽ rơi xuống, khi một thành viên giấu một thành viên khác với từ khóa 'mới' trong C# - đã quyết định đó là một vấn đề nhỏ trong hệ thống này so với vô số các tổn thương khác mà một giải pháp thích hợp cho vấn đề này nds up gây ra nếu nó đi sai (và nó nào đi sai, hãy tin tôi).

tôi chấp nhận một câu trả lời ở trên, vì nó đến gần với giải quyết vấn đề - vấn đề duy nhất nó có là nó là vẫn cấu trúc trong tự nhiên (và tôi có thể đạt được điều đó bằng cách gõ/tên/kiểm tra lập luận)

Có một số khác biệt mặc dù, chủ yếu là cần xem bản đồ giao diện thay vì gọi GetProperty - đây là một chi tiết quan trọng - đây là phương pháp được sửa đổi của tôi sẽ rơi trong một số trường hợp nhất định.

private PropertyInfo GetImplementedProperty(PropertyInfo pi) 
    { 
     var type = pi.DeclaringType; 
     var interfaces = type.GetInterfaces(); 

     for(int interfaceIndex = 0; interfaceIndex < interfaces.Length; interfaceIndex++) 
     { 
      var iface = interfaces[interfaceIndex]; 
      var interfaceMethods = type.GetInterfaceMap(iface).TargetMethods; 

      MethodInfo matchingMethod = null; 
      for (int x = 0; x < interfaceMethods.Length; x++) 
      { 
       if (pi.GetGetMethod().LooseCompare(interfaceMethods[x]) || pi.GetSetMethod().LooseCompare(interfaceMethods[x])) 
       { 
        matchingMethod = type.GetInterfaceMap(iface).InterfaceMethods[x]; 
        break; 
       } 
      } 
      if (matchingMethod == null) continue; 

      var interfacePi = from i in interfaces 
           from property in i.GetProperties() 
           where property.GetGetMethod().LooseCompare(matchingMethod) || property.GetSetMethod().LooseCompare(matchingMethod) 
           select property; 

      return interfacePi.First(); 
     } 

     return pi; 
    } 

tôi đã kết thúc từ bỏ kiểm tra xem một thành viên đang che giấu một thành viên khác, và đi hack sau:

private PropertyInfo GetRootProperty(PropertyInfo pi) 
    { 
     if ((pi.GetGetMethod().Attributes & MethodAttributes.Virtual) != MethodAttributes.Virtual) { return pi; } 

     var type = pi.DeclaringType; 

     while (true) 
     { 
      type = type.BaseType; 

      if (type == null) 
      { 
       return pi; 
      } 

      var flags = BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance | 
         BindingFlags.Public | BindingFlags.Static; 

      var inheritedProperty = type.GetProperty(pi.Name, flags); 

      if (inheritedProperty == null) 
      { 
       return pi; 
      } 

      pi = inheritedProperty; 
     } 
    } 

tôi làm cho một giả định ở đây là một tài sản/phương pháp sử dụng 'mới 'Từ khóa cũng sẽ không được sử dụng từ khóa ảo cũng được coi là' mới 'là một chút của một trường hợp cạnh tranh, điều này là khá khó xảy ra.

Điều này là xa như tôi đã nhận được trước khi quyết định rằng nó làm bài kiểm tra của tôi vượt qua và tôi đã hài lòng với điều đó. (Và trước khi tôi quyết định chỉ chọn một kiểm tra cấu trúc ...) Tôi hy vọng điều này sẽ được sử dụng cho bất kỳ ai tình cờ theo cách này trong tương lai.

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