C#迭代反思(c# Iterative reflection)

2019-10-23 03:44发布

我想设置使用反射,但使得能够访问多个子对象的设置函数值。 我有没有问题,当子对象是类,但结构它不工作。

在例子中,我有下面的类和结构

class MyConfig
{
    Gravity gravity;
}
struct Gravity
{
    Vector2 direction;
}
struct Vector2
{
    float X,Y;
}

我希望能够设置值是这样的:

MyConfig cfg=new MyConfig();
setValueViaReflection (cfg,"gravity.direction.X",76.5f);

这显然应设置cfg.gravity.direction.X到76.5f

我的代码,现在是这样的:

void setValueViaReflection (object obj,string fieldName,object value)
{
int i;
TypeInfo baseType=null;
FieldInfo field;

    string []split=fieldName.Split ('.');

    // get one subobject in each iteration
    for (i=0;i<split.Count()-1;i++)
    {
        string fname=split[i];
        baseType=obj.GetType().GetTypeInfo();

        field=baseType.GetDeclaredField (fname);
        if (field==null) return;

        obj=field.GetValue (obj);
        if (obj==null) return;

    }

    // finally you've got the final type, set value
    baseType=obj.GetType().GetTypeInfo();

    field=baseType.GetDeclaredField (split[split.Count()-1]);
    if (field==null) return;

    field.SetValue (obj,value);
}

我知道,我应该使用SetValueDirect,但使用它没有什么区别(该值在“目标文件”修改,但似乎是一个值类型,因此它不改变原来的对象。

我认为这个问题是在field.GetValue它创建一个值类型,使得最终SetValueDirect没用。

代码工作好,如果重力和Vector2设置为类。

Answer 1:

你的分析是根本上纠正。 也就是说,问题源于使用价值类型的茎。 基本的问题是,当你修改的值类型的实例,即有该实例的原始副本没有影响。 它只会改变你目前的副本,您从原始存储检索(在这种情况下,场)。 对于变化有对原始存储的效果,你就必须修改当前副本,然后商店复制回该存储。

当然,遍历字段值的路径时,这意味着,在每一个步骤,你需要修改的当前值,并将其存储回去,至少值类型。 对于引用类型字段,这在技术上是没有必要的-修改该存储的值的字段更新原有的存储-但有一个在存储旧值不伤害(即参照对象)返回到它的存储。

对我来说,这个问题似乎觉得更容易了解递归。 即你已经知道这是容易解决的基本情况,为框架直接提供机制,你用GetValue()SetValue()方法。

所以,如果你能以某种方式减少步骤,该基地的情况下这个问题,你已经解决的主要问题。 请注意,这减少的一个重要方面是,已经解决了基本情况,就需要传播导致备份领域的链条; 这意味着中间结果,所以不仅是递归这里牵连,它不会转化为纯迭代解(即它不是“尾递归”)。

换言之,不仅是递归来解决这个问题的简单方法,迭代求解仍然将需要递归的某些方面(即数据结构,就像一个堆栈)。 所以你还不如用递归,因为它是更紧凑,更容易编写(恕我直言更简单的方法来思考问题)。

下面是你想要做什么递归方法:

static void SetValueByPath(object target, string path, object value)
{
    int dotIndex = path.IndexOf('.');
    string targetProperty = dotIndex > 0 ?
        targetProperty = path.Substring(0, dotIndex) : path;
    FieldInfo fieldInfo = target.GetType().GetTypeInfo().GetDeclaredField(targetProperty);

    if (dotIndex > 0)
    {
        object currentValue = fieldInfo.GetValue(target);

        SetValueByPath(currentValue, path.Substring(dotIndex + 1), value);

        value = currentValue;
    }

    fieldInfo.SetValue(target, value);
}

请注意,虽然拳击是它的值类型字段中出现的,运行时做正确的事与他们。 您可以修改装箱值类型的字段和值类型没有新的副本; 该字段中的原始参考装箱值(即更新currentValue在上面的代码)。

还需要注意的是上面的代码工作正常,即使在混合引用类型字段。 例如,如果Gravity式是一class 。 如果你真的关心,这将有可能改变上面的代码跳过复制中间引用类型值回它的领域,因为该领域的价值本身就不会在这种情况下改变,但只是代码复杂化没有真正的效益。



文章来源: c# Iterative reflection