2009-04-07 24 views
19

tôi đang cố gắng để tạo ra một đại biểu (như là một thử nghiệm) cho:Làm cách nào để tạo đại biểu cho thuộc tính .NET?

Public Overridable ReadOnly Property PropertyName() As String 

nỗ lực trực quan của tôi đã tuyên bố các đại biểu như thế này:

Public Delegate Function Test() As String 

Và instantiating như thế này:

Dim t As Test = AddressOf e.PropertyName 

Nhưng điều này sẽ xảy ra lỗi:

Method 'Public Overridable ReadOnly Property PropertyName() As String' does not have a signature compatible with delegate 'Delegate Function Test() As String'.

Vì vậy, bởi vì tôi đã làm việc với một tài sản tôi đã cố gắng này:

Public Delegate Property Test() As String 

Nhưng điều này ném một lỗi biên dịch.

Vì vậy, câu hỏi đặt ra là làm cách nào để tôi làm đại biểu cho một thuộc tính?


Xem liên kết này:

http://peisker.net/dotnet/propertydelegates.htm

Trả lời

34

Re vấn đề sử dụng AddressOf - nếu bạn biết prop-name tại thời gian biên dịch, bạn có thể (trong C#, ít nhất) sử dụng một anon-phương pháp/lambda:

Test t = delegate { return e.PropertyName; }; // C# 2.0 
Test t =() => e.PropertyName; // C# 3.0 

Tôi không phải là Chuyên gia VB, nhưng phản xạ tuyên bố điều này giống như:

Dim t As Test = Function 
    Return e.PropertyName 
End Function 

Điều đó có hiệu quả không?


câu trả lời gốc:

Bạn tạo đại biểu cho tính với Delegate.CreateDelegate; điều này có thể được mở cho bất kỳ trường hợp nào của kiểu, được cố định cho một cá thể đơn lẻ - và có thể là cho getter hoặc setter; Tôi sẽ đưa ra một ví dụ trong C# ...

using System; 
using System.Reflection; 
class Foo 
{ 
    public string Bar { get; set; } 
} 
class Program 
{ 
    static void Main() 
    { 
     PropertyInfo prop = typeof(Foo).GetProperty("Bar"); 
     Foo foo = new Foo(); 

     // create an open "getter" delegate 
     Func<Foo, string> getForAnyFoo = (Func<Foo, string>) 
      Delegate.CreateDelegate(typeof(Func<Foo, string>), null, 
       prop.GetGetMethod()); 

     Func<string> getForFixedFoo = (Func<string>) 
      Delegate.CreateDelegate(typeof(Func<string>), foo, 
       prop.GetGetMethod()); 

     Action<Foo,string> setForAnyFoo = (Action<Foo,string>) 
      Delegate.CreateDelegate(typeof(Action<Foo, string>), null, 
       prop.GetSetMethod()); 

     Action<string> setForFixedFoo = (Action<string>) 
      Delegate.CreateDelegate(typeof(Action<string>), foo, 
       prop.GetSetMethod()); 

     setForAnyFoo(foo, "abc"); 
     Console.WriteLine(getForAnyFoo(foo)); 
     setForFixedFoo("def"); 
     Console.WriteLine(getForFixedFoo()); 
    } 
} 
+0

Cảm ơn - Im bị mắc kẹt trong .NET 2.0 cho một dự án đang được đề cập và tôi sẽ xem một công trình tương tự và phản hồi ở đây (nếu không có thể là lý do giải pháp phức tạp mà tôi liên kết đến) –

+0

rộng rãi) nhưng tôi tự hỏi nếu bạn có thể giúp đỡ với vấn đề này. Tôi cần lấy tài sản mà không cần sử dụng một chuỗi mã hóa cứng. Vấn đề là, tôi cần PropertyInfo để có được các phương pháp nhận được và tôi không thể có được điều này từ các tài sản addressOf –

+0

Cũng cảm ơn cho cách tiếp cận PropertyInfo :-) –

0

Dưới đây là một ví dụ C# nhưng tất cả các loại đều giống nhau:

Đầu tiên tạo ra các giao diện (đại biểu). Hãy nhớ rằng, một phương pháp mà bạn đính kèm với người được ủy quyền của bạn phải trả về cùng một loại và lấy các tham số giống như khai báo của người được ủy quyền của bạn. Không xác định đại biểu của bạn trong cùng phạm vi với sự kiện của bạn.

public delegate void delgJournalBaseModified();   

Làm một sự kiện dựa trên các đại biểu:

public static class JournalBase { 
    public static event delgJournalBaseModified evntJournalModified; 
}; 

Xác định một phương pháp có thể được gắn liền với sự kiện của bạn có một giao diện giống với các đại biểu.

void UpdateEntryList() 
{ 
} 

Kết nối phương thức cho sự kiện. Phương thức này được gọi khi sự kiện được kích hoạt. Bạn có thể gắn nhiều phương thức cho sự kiện của mình. Tôi không biết giới hạn. Nó có thể là một cái gì đó điên rồ.

JournalBase.evntJournalModified += new delgJournalBaseModified(UpdateEntryList); 

Điều gì xảy ra ở đây là phương thức được thêm làm gọi lại cho sự kiện của bạn. Khi sự kiện được kích hoạt, (các) phương pháp của bạn sẽ được gọi.

Tiếp theo chúng ta tạo ra một phương pháp mà sẽ cháy sự kiện khi được gọi là:

public static class JournalBase { 
    public static void JournalBase_Modified() 
    { 
    if (evntJournalModified != null) 
     evntJournalModified(); 
    } 
}; 

Sau đó, bạn chỉ cần gọi phương pháp - JournalBase_Modified() - nơi nào đó trong mã của bạn và tất cả các phương pháp gắn liền với sự kiện của bạn được gọi là quá, cái khác.

+0

Tôi đã không bỏ phiếu bạn xuống nhưng câu hỏi được đề cập đến các thuộc tính –

+1

Yea ... Tôi thấy rằng sau khi thực tế. Cảm ơn bạn đã không bỏ phiếu cho tôi. Có vẻ như tôi đã trả lời mà không kiểm tra bối cảnh của câu hỏi ... Silly me. – Will

4

Đây là một phiên bản NET C#/2.0 của Marc Gravell's response:.

using System; 
using System.Reflection; 

class Program 
{ 
private delegate void SetValue<T>(T value); 
private delegate T GetValue<T>(); 

private class Foo 
{ 
    private string _bar; 

    public string Bar 
    { 
    get { return _bar; } 
    set { _bar = value; } 
    } 
} 

static void Main() 
{ 
    Foo foo = new Foo(); 
    Type type = typeof (Foo); 
    PropertyInfo property = type.GetProperty("Bar"); 

    // setter 
    MethodInfo methodInfo = property.GetSetMethod(); 
    SetValue<string> setValue = 
    (SetValue<string>) Delegate.CreateDelegate(typeof (SetValue<string>), foo, methodInfo); 
    setValue("abc"); 

    // getter 
    methodInfo = property.GetGetMethod(); 
    GetValue<string> getValue = 
    (GetValue<string>) Delegate.CreateDelegate(typeof (GetValue<string>), foo, methodInfo); 
    string myValue = getValue(); 

    // output results 
    Console.WriteLine(myValue); 
} 
} 

Một lần nữa, 'Delegate.CreateDelegate' là những điều căn bản cho ví dụ này.

10

Tôi chỉ tạo người trợ giúp có hiệu suất khá tốt: http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.html Nó không sử dụng cách tiếp cận IL/Emit và nó rất nhanh!

Sửa bởi oscilatingcretin 2015/10/23

Nguồn chứa một số vấn đề vỏ và đặc biệt ="" mà phải được gỡ bỏ. Trước khi liên kết thối bộ, tôi nghĩ rằng tôi muốn đăng một phiên bản làm sạch của nguồn cho mì ống dễ dàng sao chép, cũng như một ví dụ về cách sử dụng nó.

nguồn Revised

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

namespace Tools.Reflection 
{ 
    public interface IPropertyAccessor 
    { 
     PropertyInfo PropertyInfo { get; } 
     object GetValue(object source); 
     void SetValue(object source, object value); 
    } 

    public static class PropertyInfoHelper 
    { 
     private static ConcurrentDictionary<PropertyInfo, IPropertyAccessor> _cache = 
      new ConcurrentDictionary<PropertyInfo, IPropertyAccessor>(); 

     public static IPropertyAccessor GetAccessor(PropertyInfo propertyInfo) 
     { 
      IPropertyAccessor result = null; 
      if (!_cache.TryGetValue(propertyInfo, out result)) 
      { 
       result = CreateAccessor(propertyInfo); 
       _cache.TryAdd(propertyInfo, result); ; 
      } 
      return result; 
     } 

     public static IPropertyAccessor CreateAccessor(PropertyInfo PropertyInfo) 
     { 
      var GenType = typeof(PropertyWrapper<,>) 
       .MakeGenericType(PropertyInfo.DeclaringType, PropertyInfo.PropertyType); 
      return (IPropertyAccessor)Activator.CreateInstance(GenType, PropertyInfo); 
     } 
    } 

    internal class PropertyWrapper<TObject, TValue> : IPropertyAccessor where TObject : class 
    { 
     private Func<TObject, TValue> Getter; 
     private Action<TObject, TValue> Setter; 

     public PropertyWrapper(PropertyInfo PropertyInfo) 
     { 
      this.PropertyInfo = PropertyInfo; 

      MethodInfo GetterInfo = PropertyInfo.GetGetMethod(true); 
      MethodInfo SetterInfo = PropertyInfo.GetSetMethod(true); 

      Getter = (Func<TObject, TValue>)Delegate.CreateDelegate 
        (typeof(Func<TObject, TValue>), GetterInfo); 
      Setter = (Action<TObject, TValue>)Delegate.CreateDelegate 
        (typeof(Action<TObject, TValue>), SetterInfo); 
     } 

     object IPropertyAccessor.GetValue(object source) 
     { 
      return Getter(source as TObject); 
     } 

     void IPropertyAccessor.SetValue(object source, object value) 
     { 
      Setter(source as TObject, (TValue)value); 
     } 

     public PropertyInfo PropertyInfo { get; private set; } 
    } 
} 

Sử dụng nó như thế này:

public class MyClass 
{ 
    public int Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public int Age { get; set; } 
} 

MyClass e = new MyClass(); 
IPropertyAccessor[] Accessors = e.GetType().GetProperties() 
    .Select(pi => PropertyInfoHelper.CreateAccessor(pi)).ToArray(); 

foreach (var Accessor in Accessors) 
{ 
    Type pt = Accessor.PropertyInfo.PropertyType; 
    if (pt == typeof(string)) 
     Accessor.SetValue(e, Guid.NewGuid().ToString("n").Substring(0, 9)); 
    else if (pt == typeof(int)) 
     Accessor.SetValue(e, new Random().Next(0, int.MaxValue)); 

    Console.WriteLine(string.Format("{0}:{1}", 
     Accessor.PropertyInfo.Name, Accessor.GetValue(e))); 
} 
+0

Người truy cập tài sản này của bạn thật tuyệt vời! Tôi dựa trên ứng dụng mới của tôi trên accessor tài sản của bạn và sau khi sửa chữa một số lỗi nhỏ, tất cả mọi thứ hoạt động như một say mê. –

+0

Trong trạng thái hiện tại đây không phải là câu trả lời rất tốt cho SO - và nên là bình luận hoặc mở rộng ... Dựa trên (10K +) câu hỏi đã xóa http://stackoverflow.com/questions/33292378/for-generic-parameters câu hỏi có một số cơ hội mà mã liên kết yêu cầu làm lại để có thể sử dụng được (và có thể không được sao chép sang SO do các mối quan tâm cấp phép). –

+0

@AlexeiLevenkov Câu hỏi bạn đã liên kết đến đã bị xóa. Ngoài ra, hãy mở rộng thêm về mối quan tâm cấp phép. Người trả lời được liên kết với blog của họ nơi họ cung cấp một ví dụ về mã. Chắc chắn, bạn không biết nếu nó yêu cầu cấp phép để sử dụng, nhưng làm thế nào để bạn biết rằng bất kỳ ví dụ mã đăng lên SE không yêu cầu cấp phép? – oscilatingcretin

1

Đây là ý tưởng tốt

Test t =() => e.PropertyName; // C# 3.0 

Nhưng chăm sóc nếu bạn đang làm một cái gì đó như thế này :

List<Func<int>> funcs = new List<Func<int>>(); 
foreach (var e in Collection) 
    funcs.Add(new Func<int>(() => e.Property)); 

Calling này:

foreach(var f in funcs) 
    f(); 

Sẽ luôn luôn trả lại giá trị tài sản của đối tượng cuối cùng trong Bộ sưu tập

Trong trường hợp này, bạn nên gọi phương thức:

foreach (var e in Collection) 
    funcs.Add(new Func<int>(e.GetPropValue)); 
0

Phiên bản VB:

Dim prop As PropertyInfo = GetType(foo).GetProperty("bar") 
Dim foo1 As New foo 

Dim getForAnyFoo As Func(Of foo, String) = TryCast([Delegate].CreateDelegate(GetType(Func(Of foo, String)), Nothing, prop.GetGetMethod()), Func(Of foo, String)) 

Dim setForAnyFoo As Action(Of foo, String) = TryCast([Delegate].CreateDelegate(GetType(Action(Of foo, String)), Nothing, prop.GetSetMethod()), Action(Of foo, String)) 

Dim getForFixedFoo As Func(Of String) = TryCast([Delegate].CreateDelegate(GetType(Func(Of String)), foo1, prop.GetGetMethod()), Func(Of String)) 

Dim setForFixedFoo As Action(Of String) = TryCast([Delegate].CreateDelegate(GetType(Action(Of String)), foo1, prop.GetSetMethod()), Action(Of String)) 

    setForAnyFoo(foo1, "abc") 
    Debug.WriteLine(getForAnyFoo(foo1)) 

    setForFixedFoo("def") 
    Debug.WriteLine(getForFixedFoo()) 
Các vấn đề liên quan