简体   繁体   中英

In C# can I create a delegate instance to an auto-implemented property accessor?

Is it possible to do something like this:

class Foo
{
  public delegate void Setter(float value);
  public float Mass { get; set; }

  public Foo()
  {
    Setter massSetter = new Setter(Mass.set);
    // massSetter would be stored somewhere and used later
  }
}

Can it be done while still using auto-implemented getters and setters?

Edit: I am trying to build a game using an "Entity Component" system, each "thing" in the game is an entity, and each entity is made up of components. Components are things like drawing to the screen, processing input, physics stuff, etc. One key in my plan for building these components is that all configuration of them will be done using their properties. Then I want to be able to save, load, duplicate, and edit on the fly these Components in a generic way. If the meat of the components is in their properties, then to save we just save the value of each property, same for loading and duplication. And when I add a new component, getting it to have all of these desired behaviors will be as simple as adding all the getters, setters, and property names, to a list. This will also allow me to build a generic editor which will allow me to edit any property on the fly. (Yes I realize I will have to handle all string properties differently from all int properties but I am only planning on using a handful of types.) I hope that made sense and I am open to suggestions on alternate designs.

As some comments hint, this will do the job:

  class Foo
  {
      public delegate void Setter(float value);
      public float Mass { get; set; }

      public Foo()
      {
          Setter massSetter = x => Mass = x;
          // massSetter would be stored somewhere and used later
      }
  }

IE, use an action as a delegate.

In this case though, it seems a bit superfluous, why do you need this functionality?

var f = new Foo();
f.Mass = 1.0d; // What you do with the property

theSetter(1.0d); // What you can do now

Isn't the property clearer in it's purpose?

Some time ago I made this objectification of a property which allows passing it around, getting and setting the value.

using System;
using System.Linq.Expressions;
using System.Reflection;

public class PropHandle<TOwner, TProperty>
{
    private TOwner owner;
    private PropertyInfo propertyInfo;

    public PropHandle(TOwner owner, Expression<Func<TOwner, TProperty>> propertyExpression)
    {
        this.owner = owner;

        var memberExpression = (MemberExpression)propertyExpression.Body;
        this.propertyInfo = (PropertyInfo)memberExpression.Member;
    }

    public TProperty Get()
    {
        return (TProperty)this.propertyInfo.GetValue(this.owner, null);
    }

    public void Set(TProperty value)
    {
        this.propertyInfo.SetValue(this.owner, value, null);
    }
}

Together with a helpful extension method

public static PropHandle<TOwner, TProperty> PropHandle<TOwner, TProperty>(this TOwner owner, Expression<Func<TOwner, TProperty>> propertyExpression)
{
    return new PropHandle<TOwner, TProperty>(owner, propertyExpression);
}

Created and used like this

var myProp = this.PropHandle(x => x.Mass);
myProp.Set(12);

And used in a method

public void MyMethod<TOwner>(PropHandle<TOwner, float> massProperty)
{
    massProperty.Set(123);
}

EDIT: To get the name and path (one level) of the property you can add this method to the PropHandle-class

public string GetSimplePath()
{
    return this.owner.GetType().Name + this.propertyInfo.Name;
}

flindeberg's answer is the simplest and the clearest. But, if you're dead-set (no pun intended) on using the setter method without wrapping it in a lambda, this can be done. It's just not the easiest thing in the whole world.

using System;
using System.Linq.Expressions;
using System.Reflection;

public Action<TProperty> GetSetterAction(TSource target,
     Expression<Func<TSource, TProperty>> propertyExpression)
{
    var memberExpression = propertyExpression.Body as MemberExpression;
    if (memberExpression == null)
    {
        throw new ArgumentException(@"propertyExpression must express a single
            member of a type.", "propertyExpression");
    }

    var propertyInfo = memberExpression.Member as PropertyExpression;
    if (propertyInfo == null)
    {
        throw new ArgumentException(@"propertyExpression must express a single
           property of a type.", "propertyExpression");
    }

    var setMethodInfo = propertyInfo.GetSetMethod();
    if (setMethodInfo == null)
    {
        throw new ArgumentException(@"propertyExpression must express a single
            writeable property of a type.", "propertyExpression");
    }

    var setMethodDelegate = setMethodInfo.CreateDelegate(
        typeof(Action<TProperty>), target);

    return (Action<TProperty>)(object)setMethodDelegate;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM