Initializing autoproperties of object using Reflec

2019-05-18 08:55发布

I recently wrote two classes and an interface as a way to implement the answer to this question of mine.

The first class is the Notifier generic class:

public interface INotifier { }
public class Notifier<T> : Observable,INotifier where T:new()
{
    public Notifier()
    {            
        V = new T();
    }
    private T _ValueBacker;
    public T V
    {
        get { return _ValueBacker; }
        set
        {
            _ValueBacker = value;
            OnPropertyChanged(() => V);
        }
    }
}

The Observable base class here is just a class that implements INotifyPropertyChanged and defines an OnPropertyChanged method.

Thanks to that class, I can now define a Silverlight/WPF ViewModel like this:

public class Person : ViewModelBase
{
    Notifier<string> Name {get;set;}
    Notifier<string> Surname {get;set;}
    Notifier<int> Age {get;set;}
}

instead of:

public class Person : Observable
{
    private string _Name;
    public string Name 
    {
        get
        {
            return _Name;
        }
        set
        {
            _Name=value;
            OnPropertyChanger(()=>Name);
        }
    }
    privaate string _Surname;
    public string Surname 
    {
        get
        {
            return _Surname;
        }
        set
        {
            _Surname=value;
            OnPropertyChanger(()=>Surname);
        }
    }
    private int _Age;
    public int Age
    {
        get
        {
            return _Age;
        }
        set
        {
            _Age=value;
            OnPropertyChanger(()=>Age);
        }
    }
}

As you can see, the new code is much more concise and much less coding-error (or typo) prone. All I have to do in my XAML is to bind to "MyPerson.V" instead of "MyPerson". However, since there aren't any ways to implement initializers for autoproperties, I had to initialize every property in the constructor. In some cases, I skipped the initializers and that led to some runtime errors. So, to take care of that, in the constructor of the ViewModelBase class, I added this loop:

    public ViewModelBase()
    {
        foreach(var notifierProperty in this.GetType().GetProperties().Where(c=>c.PropertyType.GetInterfaces().Any(d=>d==typeof(INotifier))))
        {                
            notifierProperty.SetValue(this, notifierProperty.PropertyType.GetConstructor(System.Type.EmptyTypes).Invoke(null), null);
        }
    }

What this does is, whenever you instantiate a ViewModelBase derived class, the constructor loops through the properties, and invokes the constructor for each Notifier type property.

Is this evil? Will using reflection this way come back to haunt me in the future? Are there any performance hits I should be aware of?

1条回答
倾城 Initia
2楼-- · 2019-05-18 08:56

I think that's fine. I have some bits of information to add:

  1. You can create types with trivial constructors by calling Activator.Create(myType), which means you don't have to fetch a constructor.
  2. I believe at least for Silverlight, all properties initialized with your hack need to be public.
  3. There is a library called ReactiveProperty, that defines a class ReactiveProperty<T> very similar to your Notifier<T>.

You will bind against it's Value property:

public class ReactiveProperty<T> : IObservable<T>, IDisposable, INotifyPropertyChanged, IReactiveProperty, INotifyDataErrorInfo
{
    public T Value
    {
        get { return latestValue; }
        set { anotherTrigger.OnNext(value); }
    }

    // ...
}

The call in the setter eventually leads to the respective call to INotifyPropertyChanged.PropertyChanged.

ReactiveProperty<T> also is an observable in the sense of reactive extensions, on which the library depends. Other than that, the author basically does what you do, but without the initialization hack in the constructor.

查看更多
登录 后发表回答