【问题标题】:C# - How to get/set an object's property value without using ReflectionC# - 如何在不使用反射的情况下获取/设置对象的属性值
【发布时间】:2013-09-15 01:08:13
【问题描述】:

我需要能够存储对对象属性的引用,以便以后读取/写入,最好不使用反射。我也对一种也可以使用的设计模式持开放态度,但性能至关重要,因为我的应用程序将在各种“父”类上进行数百万次“运行”调用。下面的代码模板应该解释我想要做什么。

我想保留它,以便我“需要”的变量是 Child 类的对象属性,而不是存储在某个列表中的数据结构,例如。

最后,我想我正在寻找的不仅仅是重置对象值或检查非空值。例如,在调用 _run 之后,Parent 可能会将属性的值用于其他用途。

谢谢。

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";
    }
}

【问题讨论】:

  • 什么版本的 .NET 框架?
  • 4.5 - 这是满足字符配额的额外文本。

标签: c# properties


【解决方案1】:

我会使用接口,而不是使用 an 属性。

创建一个接口IValidatable并将其放在父级上。
给父母一个它的抽象实现。 并在孩子身上实施。

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();
}

【讨论】:

  • 我希望 Parent 类尽可能多地处理这个问题,并且不必假设 sublcasses 正在正确地完成他们的工作。 run() 的一部分工作是将所有“必需”属性设置为 null,并在 _run 之后进行验证。在您的解决方案中,我必须在接口中添加一个“重置”方法并在 _run 之前调用它,这意味着现在每个子类都需要实现两个单调的接口方法,而不仅仅是在属性上放置一个属性。
  • 是的,我所有的想法都需要在 Child 类上进行繁重的工作。
  • 这可能是不可能的,我将不得不处理反射的成本。我希望我可以存储一个指向对象属性的指针,或者类似的东西。感谢您的努力。
  • 好吧,如果你坚持不做反思的话。代码生成和 T4 模板可能需要研究。你可以让它根据类的属性生成你的验证方法。
【解决方案2】:

我找到了一个运行良好的高性能反射库: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";
    }
}

【讨论】:

  • OP 要求提供不使用反射的解决方案。
  • @KarlAnderson 他是 OP,所以我想他可以为所欲为。
  • @mikez - 我认为你错过了我的意思,你提供了一个 OP 不想要的解决方案。
  • 由于性能问题,我要求不使用反射——但是使用反射的高性能解决方案很好。
  • 注意:这只是将反射移动到库中(尽管它至少通过元编程进行了额外的步骤,而不仅仅是原始反射)
【解决方案3】:

另一个解决方案使用 Wrapper 对象——这样我们可以存储对这些对象的引用并在以后获取/设置它们。为方便起见,Wrapper 实例可以由基础对象使用反射自动创建。唯一的问题是在 _run() 里面你必须做 value1.value = "some value" 而不是 value1 = "some value"。公平交易,IMO。

这个解决方案只需要在实例化时进行反射,并且仍然保持类型(为了 Intellisense 的优点)

代码看起来像这样.. 还没有测试过,但这是一般的想法。

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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    • 2018-07-27
    • 1970-01-01
    相关资源
    最近更新 更多