【问题标题】:How to set value of property where there is no setter如何在没有设置器的情况下设置属性值
【发布时间】:2014-01-07 01:26:39
【问题描述】:

我已经看到提出和回答的各种问题,我们可以使用反射调用私有设置器,例如这个:

Is it possible to get a property's private setter through reflection?

但是,我有一些代码具有我需要设置的属性,但由于没有设置器,我无​​法添加设置器,因为这不是我的代码。有没有办法在这种情况下使用反射来设置值?

【问题讨论】:

  • 属性没有值。他们可能有设置器,可以改变另一个字段的值。如果您有兴趣查找自动生成属性的支持字段,请参阅以下问题:stackoverflow.com/questions/8817070/…

标签: c# reflection properties automatic-properties


【解决方案1】:

@abyte0 的回答中添加一个实际用例。

一些库使用反射来设置属性。例如,请参阅https://github.com/natemcmaster/CommandLineUtils 中的此示例代码:

using System;
using McMaster.Extensions.CommandLineUtils;

public class Program
{
    public static int Main(string[] args)
        => CommandLineApplication.Execute<Program>(args);

    [Option(Description = "The subject")]
    public string Subject { get; } = "world";

    [Option(ShortName = "n")]
    public int Count { get; } = 1;

    private void OnExecute()
    {
        for (var i = 0; i < Count; i++)
        {
            Console.WriteLine($"Hello {Subject}!");
        }
    }
}

在幕后,这个语法是用this code实现的:

        public static SetPropertyDelegate GetPropertySetter(PropertyInfo prop)
        {
            var setter = prop.GetSetMethod(nonPublic: true);
            if (setter != null)
            {
                return (obj, value) => setter.Invoke(obj, new object?[] { value });
            }
            else
            {
                var backingField = prop.DeclaringType.GetField($"<{prop.Name}>k__BackingField", DeclaredOnlyLookup);
                if (backingField == null)
                {
                    throw new InvalidOperationException(
                        $"Could not find a way to set {prop.DeclaringType.FullName}.{prop.Name}. Try adding a private setter.");
                }

                return (obj, value) => backingField.SetValue(obj, value);
            }
        }

这里的实际价值是让代码表达设置值的唯一方法是通过命令行调用。这是允许的:hello.exe -s world,但这不是:Subject = "some other value";

【讨论】:

    【解决方案2】:

    我不建议在您的应用程序上执行此操作,但出于测试目的,它可能有用...

    假设你有:

    public class MyClass
    {
         public int MyNumber {get;}
    }
    

    如果出于测试目的,您可以这样做,我不建议在您的运行时代码中使用它:

    var field = typeof(MyClass).GetField("<MyNumber>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
    field.SetValue(anIstanceOfMyClass, 3);
    

    【讨论】:

    • 谢谢。我试图更改仅 getter 属性的值,但绝对无法为该属性添加 setter - 这很有帮助。
    • 这也是我的想法。答案应该是:“尝试找到一个包含您要更改的实际数据的字段。
    • 目前仍在工作,这个答案对我来说是正确的;)
    【解决方案3】:

    您必须记住,属性只是一对方法的语法糖。一种方法(getter)返回属性类型的值,一种方法(setter)接受属性类型的值。

    不要求 getter 和 setter 实际获取或设置任何东西。它们只是方法,所以它们可以做任何事情。唯一的要求是 getter 返回一个值。从外部看,您无法真正判断是否有支持领域。每次调用 getter 时都会计算它。它可能基于其他属性。

    所以,不,实际上没有任何方法可以“设置”没有设置器的属性。

    【讨论】:

    • 这是错误的。使用反射,您可以设置支持字段。问题是知道支持字段是什么。
    • @Salgat 重点是属性不需要有后备字段,因此您一般不能“知道后备字段”,因为它可能不存在。例如,public int Property { get { return 5; } } 完全有效并且没有支持字段。此外,一个属性可能依赖于多个支持字段,这进一步混淆了能够“设置”它的含义。例如。 public double Length { get { return Math.Sqrt(this.x * this.x + this.y * this.y); } }.
    • 这就是反思的重点。您可以使用 type.GetField 方法之类的方法来确定支持字段是否存在(如果不存在则返回 null)。 OP明确指出,可以使用反射来设置值吗,通常是可以的。
    • @Salgat Kyle 的回答清楚地表明没有通用方法可以做到这一点,他是正确的。
    • 有一种通用的方法,但有一个例外(当属性没有自动实现时)。但在这一点上,我们正在争论语义。
    猜你喜欢
    • 2011-11-30
    • 2011-05-01
    • 2019-06-16
    • 1970-01-01
    • 1970-01-01
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多