如何在不实例化对象的情况下获取 PropertyInfo 的默认值?

3

我有一个可用的代码(简化版):

Assembly lib = Assembly.LoadFile("C:\\mydll.dll");
var libType = lib.GetTypes();
Type mvType = libType[0];
PropertyInfo WidthProperty = mvType.GetProperty("Width");

但是,我希望能够使用类似以下代码访问默认值:

var WidthPropertyValue = WidthProperty.GetValue(???, null);

问题在于我们无法使用Activator.CreateInstance来实例化对象,因为构造函数需要整个庞大的项目加载才能工作... 不管怎样,绕过这个问题不是重点。

问题是,使用此策略是否可以访问默认方式?还有其他方法吗?完全不可能吗?


4
你在说的“默认值”是指设计师通过属性遵循的默认值,还是构造函数中分配的初始值?这里提供一个例子会更有帮助。 - Jon Skeet
1
我会说构造函数的初始值,但我可以在不实例化对象的情况下访问所有这些默认值?!? - Guillaume Slashy
@GuillaumeSlashy:如果值是由代码分配的,那么它怎么可能在不执行该代码的情况下被评估呢?也许这段代码使用了 DateTime.Now,或者进行了 Web 服务调用... - Jon Skeet
1
你能不能使用类似这里提供的解决方案?https://dev59.com/FnRC5IYBdhLWcg3wW_pk - Kralizek
@Kralizek:这会根据属性类型给出默认值,但这可能与类中定义的默认值无关。属性的默认值是实现特定的,而不是类型特定的,它在许多情况下可能相同,但并不一定相同。 - InBetween
显示剩余2条评论
3个回答

3

你需要澄清“默认值”的含义...它是属性类型的默认值吗?如果是这样,对于引用类型,它是null,对于值类型,它是使用默认构造函数创建的实例:

static object GetDefaultValue(PropertyInfo prop)
{
    if (prop.PropertyType.IsValueType)
        return Activator.CreateInstance(prop.PropertyType);
    return null;
}

如果您指的是“使用DefaultValueAttribute声明的值”,则可以通过以下方式检索它:
static object GetDefaultValue(PropertyInfo prop)
{
    var attributes = prop.GetCustomAttributes(typeof(DefaultValueAttribute), true);
    if (attributes.Length > 0)
    {
        var defaultAttr = (DefaultValueAttribute)attributes[0];
        return defaultAttr.Value;
    }

    // Attribute not found, fall back to default value for the type
    if (prop.PropertyType.IsValueType)
        return Activator.CreateInstance(prop.PropertyType);
    return null;
}

如果你想获取在构造函数中分配给属性的值,除了创建类实例之外没有任何方法可以做到这一点(嗯,你可以反编译代码,但这并不容易...)。

1
如果可以访问汇编代码,我建议使用Attribute定义默认值。
否则,我认为这是不可能的。属性默认值不是程序集元数据的一部分(默认值概念并不存在,这就是为什么有DefaultValueAttribute),所以我不确定除了创建对象实例之外是否能够找到它。它可能是任何东西:后备字段的默认值、构造函数中设置的某个值、基于某些环境条件等等。 更新:我看到了一些答案/评论指向创建属性类型的默认类型。属性默认值是实现特定的,而不是类型特定的。它们在许多情况下可能重合,但不一定如此。

是的,这就是我的问题所在,但我感觉这是不可能的,我希望能有一个神奇的解决方案,因为有时候这里会发生这种情况 :) 关于编辑源代码,我们不能这样做,但我会记住这个建议,以备将来之需! - Guillaume Slashy

1

如果值是在构造函数中设置的,而且你实际上不能调用构造函数,那么提取该值就不容易。这是因为该值实际上嵌入在构造函数的代码中。

如果你真的很勇敢,并且有很多时间,你可以尝试使用类似 Cecil 的工具直接检查 IL。如果属性是一个简单类型(int、double、string 等),那么你可以执行以下操作:

如果通过构造函数中的字段进行设置:

  1. 确定属性的字段名称。您可以根据编码约定猜测名称(不太美观,但根据工具的意图可能会完成工作),或者您可以进一步检查属性 getter 以查看它使用哪些字段。即使属性不仅返回值,这也可能很棘手。

  2. 检查构造函数代码并查找对该字段的引用。如果构造函数调用设置默认值的其他方法,则可能会变得复杂。

  3. 找到感兴趣的字段的 Stfld 后,立即在其上方查找常量值(Ldc_I4、Ldc_R4、Ldc_R8 或 Ldstr)。

如果通过构造函数中的属性进行设置:

  1. 确定属性 setter 并查找代码中对 setter 的调用。

  2. 与字段情况一样,在调用之前立即检查已加载的常量值。

如果你真的想尝试这个,我建议先创建各种类型的构造函数并在ildasm中检查代码,以了解编译后代码的样子。在一般情况下自动确定默认值将非常困难,但对于“常见”情况来说是可以实现的,这可能已经足够满足你的需求了。

我的 PropertyInfo 的 Type 是 Double,但是你所说的显然超出了我们想要做的范围 :) 不过我一定会看一下的。 - Guillaume Slashy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接