简体   繁体   中英

C# - How to get/set an object's property value without using Reflection

I need to be able to store a reference to an object's property in order to read/write it later, ideally without using reflection. I'm also open to a design pattern that works too, but performance is paramount as my application will be making millions of calls to "run" on various "Parent" classes. The code template below should explain what I'm trying to do.

I'd like to keep it such that the variables I am "requiring" are object properties of the Child class, and not data structures stored in some list, for example.

Finally, I think I'm looking for something that extends just beyond resetting object values or checking for not null. For example, after calling _run, Parent might use the values of the properties for something else.

Thank you.

class requiredAttribute : Attribute
{
}

abstract class Parent
{
    abstract protected void _run();

    public Parent() {
        // This can do whatever set-up is necessary in order to make the below run() call
        // not need reflection.

        /* What code goes here? */
    }

    public void run() {
        // This code should reset all 'required' properties to null.
        /* What goes here? */

        _run();

        // This code needs to ensure any required property is now not null.
        // If it finds a null one, it should throw.
        /* What goes here? */
    }
}

class Child : Parent
{
    [required]
    protected object value1;
    [required]
    protected object value2;

    // not required..
    protected object value3;

    protected void _run() {
        // This must set all 'required' properties' values, otherwise the Parent should throw.
        value1 = "some value";
        value2 = "some other value";
    }
}

Rather than using the an attribute, I would use an interface.

Create an interface IValidatable and place it on the parent.
Give the parent an abstract implementation of it. And implement it on the child.

abstract class Parent : IValidatable
{
    public abstract bool IsValid();
    abstract protected void _run();

    public Parent()
    {
    }

    public void run()
    {
        _run();

        if (!IsValid())
            //throw
    }
}

class Child : Parent
{
    protected object value1;
    protected object value2;

    // not required..
    protected object value3;

    protected override void _run()
    {
        // This must set all 'required' properties' values, otherwise the Parent should throw.
        value1 = "some value";
        value2 = "some other value";
    }

    public override bool IsValid()
    {
        return value1 != null && value2 != null;
    }
}

public interface IValidatable
{
    bool IsValid();
}

I found a high-performance Reflection library that works well: FastMember .

using FastMember;

class RequiredAttribute : Attribute
{
}

abstract class Parent
{
    abstract protected void _run();

    private List<string> required_prop_names = new List<string>();
    private ObjectAccessor accessor;

    public Parent()
    {
        // create list of properties that are 'required'
        required_prop_names = this.GetType().GetFields()
            .Where(prop => Attribute.IsDefined(prop, typeof(RequiredAttribute)))
            .Select(prop => prop.Name)
            .ToList<string>();

        // create FastMember accessor
        accessor = ObjectAccessor.Create(this);
    }
    public void run()
    {
        // set all to null
        required_prop_names.ForEach(x => accessor[x] = null);
        // call child
        _run();
        // validate
        foreach (string prop_name in required_prop_names){
            Console.WriteLine("Value of {0} is {1}", prop_name, accessor[prop_name]);
            if (accessor[prop_name] == null){
                Console.WriteLine("Null value found on {}!", prop_name);
            }
        }
    }
}

class Child : Parent
{
    [Required]
    public object value1 = "something";
    [Required]
    public object value2 = "something";

    // not required..
    public object value3;

    override protected void _run()
    {
        // This must set all 'required' properties' values, otherwise the Parent should throw.
        value1 = "something else";
        //value2 = "something else";
    }
}

Another solution uses Wrapper objects -- this way we can store references to those objects and get/set them later. For convenience the Wrapper instances can be created automatically by the base object, using reflection. The only catch is inside _run() you must do value1.value = "some value" rather than value1 = "some value". A fair trade, IMO.

This solution only requires reflection upon instantiation and still maintains types (for Intellisense goodness)

Code looks something like this.. haven't tested it, but it's the general idea.

class Wrapper {
   public object value;
}
class Wrapper<T> : Wrapper {
   new public T value;
}

class requiredAttribute : Attribute
{
}

abstract class Parent
{
    abstract protected void _run();

    private List<Wrapper> wrappers = new List<Wrapper>();

    public Parent() {
        // Use reflection to find all fields that have a 'required' attribute.
        // For each one, instantiate an instance of Wrapper<T>
        // Store the reference to each object so that in run() we can reset and validate.

        /* code that does above statement goes here. */
    }

    public void run() {
        foreach (Wrapper wrapper in wrappers){
           wrapper.value = null;
        }
        _run();
        foreach (Wrapper wrapper in wrappers){
           if (wrapper.value == null) throw new Exception("null value found");
        } 
    }
}

class Child : Parent
{
    [required]
    protected Wrapper<string> value1;
    [required]
    protected Wrapper<int> value2;

    // not required..
    protected object value3;

    protected void _run() {
        // This must set all 'required' properties' values, otherwise the Parent should throw.
        value1.value = "some value";
        value2.value = 123;
    }
}

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