2009-12-31 26 views
9

Nói rằng tôi có một lớp học với một tài sảnmạnh Gõ một tên thuộc tính trong .NET

Public Class MyClass 
    Public Property MyItem() as Object 
     .... 
    End Property 
End Class 

tôi phải vượt qua tên của tài sản để một lời gọi hàm. (Xin đừng hỏi tại sao nó nên được thực hiện theo cách này, một khuôn khổ của bên thứ ba). Ví dụ:

SomeFunc("MyItem") 

Nhưng điều tôi muốn làm là thay đổi chuỗi thành thông số được nhập mạnh mẽ. Có nghĩa là, nếu tên thuộc tính được đổi tên hoặc thay đổi, nó cũng sẽ được phản ánh ở đây.

Vì vậy, một cái gì đó thuộc loại này:

Dim objectForStrongTyping as New MyClass() 
SomeFunc(objectForStrongTyping.MyItem().Name()) 

Tôi chắc chắn điều này sẽ không làm việc. Có cách nào để đánh máy mạnh mẽ này có thể được thực hiện không? (C# hoặc VB.NET, bất cứ điều gì là mát mẻ)

Trả lời

20

Đây là giải pháp sử dụng các lớp học từ System.Linq.Expressions.

static MemberInfo GetMemberInfo<TObject, TProperty>(
    Expression<Func<TObject, TProperty>> expression 
) { 
    var member = expression.Body as MemberExpression; 
    if (member != null) { 
     return member.Member; 
    } 

    throw new ArgumentException("expression"); 
} 

Chỉ cần ném lớp này vào một nơi nào đó (ExpressionHelper?).

Cách sử dụng:

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

MemberInfo member = GetMemberInfo((SomeClass s) => s.SomeProperty); 
Console.WriteLine(member.Name); // prints "SomeProperty" on the console 
+0

+1 Để chụp nhanh nhanh chóng! –

+0

Đẹp, điều này có thể được bao bọc thành một phần mở rộng? – ChaosPandion

0

Bạn luôn có thể sử dụng một lớp tĩnh có chứa string hằng thay vì đi qua trong một chữ string:

public static class ObjectForStrongTyping 
{ 
    public const string MyItem = "MyItem"; 
    public const string MyOtherItem = "MyOtherItem"; 
    // ... 
} 

Mã của bạn sau đó sẽ trở thành:

SomeFunc(ObjectForStrongTyping.MyItem); 
+0

+1: trong khi đó không phải là tài liệu s "tự động" như các giải pháp LINQ và typeof (...), siêu đơn giản và bảo trì của nó là tối thiểu. – Juliet

1

Nếu chỉ có một tài sản mà bạn có thể làm điều này - có được thông tin thuộc tính trên tài sản đầu tiên của lớp:

//C# syntax 
typeof(MyClass).GetProperties()[0].Name; 

'VB syntax 
GetType(MyClass).GetProperties()(0).Name 

EDIT Biến ra, nơi bạn có thể sử dụng biểu thức, bạn cũng có thể sử dụng projection cho loại phản ánh này (mã C#).

public static class ObjectExtensions { 
    public static string GetVariableName<T>(this T obj) { 
     System.Reflection.PropertyInfo[] objGetTypeGetProperties = obj.GetType().GetProperties(); 

     if(objGetTypeGetProperties.Length == 1) 
      return objGetTypeGetProperties[0].Name; 
     else 
      throw new ArgumentException("object must contain one property"); 
    } 
} 

class Program { 
    static void Main(string[] args) { 
     Console.WriteLine(Console.WriteLine(new { (new MyClass()).MyItem}.GetVariableName());); 
    } 
} 

Với giải pháp này, lớp học có thể có bất kỳ số lượng thuộc tính nào, bạn có thể nhận được bất kỳ tên nào khác.

+0

Ouch. Bây giờ thay vì lo lắng về việc thay đổi Properties, chúng ta phải lo lắng về việc thêm/sắp xếp lại các thuộc tính? – Greg

+0

Vâng, nó khá bẩn, nhưng theo OP, lớp chỉ có một tài sản. Xem chỉnh sửa để có giải pháp tốt hơn. –

+0

Với EDIT, đây là một câu trả lời rất thú vị. Phương pháp chiếu là tốt đẹp. Có đề xuất nào cho người dùng có ý nghĩa hơn không? Trong VB.Net cú pháp kiểu vô danh là terse hơn cú pháp lambda. – mrmillsy

3

giải pháp này hoạt động trong cả hai C# và VB.NET, nhưng cú pháp VB.NET cho các chức năng lambda là không sạch, mà có lẽ sẽ làm cho giải pháp này kém hấp dẫn trong VB. Ví dụ của tôi sẽ có trong C#.

Bạn có thể đạt được hiệu quả bạn muốn sử dụng chức năng lambda và cây biểu hiện tính năng của C# 3. Về cơ bản, bạn sẽ viết một hàm wrapper gọi SomeFuncHelper và gọi nó là như thế này:

MyClass objForStrongTyping = new MyClass(); 
SomeFuncHelper(() => objForStrongTyping.MyItem); 

SomeFuncHelper được thực hiện như sau:

void SomeFuncHelper(Expression<Func<object>> expression) 
{ 
    string propertyName = /* get name by examining expression */; 
    SomeFunc(propertyName); 
} 

Biểu thức lambda () => objForStrongTyping.MyItem được dịch sang đối tượng Expression được chuyển đến SomeFuncHelper. SomeFuncHelper kiểm tra Expression, rút ​​ra tên thuộc tính và gọi SomeFunc.Trong thử nghiệm nhanh của tôi, đoạn code sau làm việc để lấy tên thuộc tính, giả định SomeFuncHelper luôn gọi như trình bày ở trên (tức là () => someObject.SomeProperty):

propertyName = ((MemberExpression) ((UnaryExpression) expression.Body).Operand).Member.Name; 

Có thể bạn sẽ muốn đọc lên trên cây biểu hiện và làm việc với để làm cho nó mạnh mẽ hơn, nhưng đó là ý tưởng chung.

Cập nhật: này tương tự như giải pháp của Jason, nhưng cho phép các biểu thức lambda bên trong gọi helper-chức năng để có một chút đơn giản (() => obj.Property thay vì (SomeType obj) => obj.Property). Tất nhiên, điều này chỉ đơn giản hơn nếu bạn đã có một thể hiện của kiểu ngồi xung quanh.

0

Giải pháp tốt nhất tôi nghĩ là tạo các hằng số tĩnh bằng T4 (ví dụ: T4MVC).

public static class StaticSampleClass 
{ 
    public const string MyProperty = "MyProperty"; 
} 

Hãy tin tôi khi bạn có nhiều cuộc gọi phản ánh và biểu thức LINQ đang giảm hiệu suất của ứng dụng.

Điều xấu là T4 đã biến mất trong lõi thuần. :(

Điều tốt trong C# 6.0 u có thể sử dụng nameof(SampleClass.MyProperty)

Trong trường hợp xấu nhất u có thể sử dụng các ví dụ sau:

using System.Linq.Expressions; 

namespace ConsoleApp1 
{ 
    public static class Helper 
    { 
     public static string GetPropertyName<T>(Expression<Func<T, object>> propertyExpression) 
     { 
      var member = propertyExpression.Body as MemberExpression; 
      if (member != null) 
       return member.Member.Name; 
      else 
       throw new ArgumentNullException("Property name not found."); 
     } 
     public static string GetPropertyName<T>(this T obj, Expression<Func<T, object>> propertyExpression) 
     { 
      return GetPropertyName(propertyExpression); 
     } 
    } 

    public class SampleClass 
    { 
     public string MyProperty { get; set; } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Property name of type 
      Console.WriteLine(Helper.GetPropertyName<SampleClass>(x => x.MyProperty)); 

      // Property name of instance 
      var someObject = new SampleClass(); 
      Console.WriteLine(someObject.GetPropertyName(x => x.MyProperty)); 

      Console.ReadKey(); 
     } 
    } 
} 

kết quả Hiệu suất (1 triệu lần gọi):

StaticSampleClass.MyProperty - 8 ms

nameof(SampleClass.MyProperty) - 8 ms

Helper.GetPropertyName<SampleClass>(x => x.MyProperty) - 2000 ms

3

Trong C# 6.0 Có một tính năng mới có tên là nameof. Về cơ bản bạn có thể làm điều này:

var name = nameof(MyClass.MyItem); 

Nhìn vào Telerik chuyển đổi mã từ C# để VB có vẻ như đây là VB tương đương:

Dim name = nameof([MyClass].MyItem) 

Vì vậy, bạn có thể làm như sau:

SomeFunc(nameof(MyClass.MyItem)); 

Dưới đây là tham chiếu đến tài liệu microsoft: https://docs.microsoft.com/en-us/dotnet/articles/csharp/language-reference/keywords/nameof

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