是否可以在没有 setter 的情况下在 属性 中设置值?

Is is possible to set value in property without a setter?

我正在使用此包在 .net 应用程序中实现 mandrill (https://github.com/shawnmclean/Mandrill-dotnet) 现在有一个模型 "EmailMessage" 用于发送电子邮件。在 EmailMessage 模型内部有一个 属性 "GlobalMergeVars":

   [JsonProperty("global_merge_vars")]
   public List<MergeVar> GlobalMergeVars { get; }

现在我们可以看到 GlobalMergeVars 没有 setter,但我必须为此设置一个值。如果我们转到 link (https://mandrillapp.com/api/docs/messages.html) 并单击 "Try it" 按钮,那么我们可以为 GlobalMergeVars 设置值,但我认为我使用的包存在一些问题。但是我不能切换到其他包,因为与其他包相比它非常灵活。那么有没有办法为 GlobalMergeVars 设置值?

PS:我还在 github 回购上打开了一个问题。但由于截止日期很近,我需要快速修复。

很抱歉成为坏消息的传递者,但是如果没有 public setter,则不能直接从 class 外部设置值。其他选项将通过构造函数或允许这样的方法。但是您似乎没有任何可用的选项。

附带说明一下,只需检查一下 属性 是否可能没有可以作为一个整体设置的父 属性。例如在 C# 中使用字体时,例如设置标签的字体。 Font 对象的一些属性是 "read-only" 并且不能在外部设置。但是可以设置 Font 对象本身。因此,在这种情况下,您必须在本地创建一个新的 Font 对象并设置所需的道具,然后 assign/set 作为新的 Font。

希望您能及时找到解决方法。

更新:

Rufo 先生刚刚给了你解决方案:

The answer is: read at github.com/shawnmclean/Mandrill-dotnet/blob/master/src/Mandrill/… starting from line 376. That method is designed for your case

我想我找到了您所说的 class here。尽管 EmailMessage 的 属性 给出的与您在问题中显示的不同:

public class EmailMessage
{
    #region Public Properties
    ...
    /// <summary>
    ///   Gets the global_merge_vars.
    /// </summary>
    public List<MergeVar> GlobalMergeVars { get; private set; }

(此处的代码与问题中给出的代码之间的差异无关紧要 - 我只是想知道我是否正在查看正确的文件!)

缺少的set相当于private set。 class 中有以下方法,可让您设置变量:

public void AddGlobalVariable(string name, dynamic content)
{
  if (GlobalMergeVars == null)
  {
    GlobalMergeVars = new List<MergeVar>();
  }

  var mv = new MergeVar {Name = name, Content = content};
  GlobalMergeVars.Add(mv);
}

我知道您已经为您的特定案例找到了答案,但由于问题是“是否可以在没有 setter 的情况下在 属性 中设置值?" 并且明确地说你不能直接从 class 之外设置值,我只是想提供一个这样做的例子......

如果您愿意使用反射,您实际上可以从 class 外部设置值。

你只是需要有点技巧。

您需要滥用这样一个事实,即为 Auto 属性创建的私有字段是由编译器脱糖而不是由 JITer 创建的。

这意味着在程序集中实际上会有一个这样命名的私有字段:<[NameOfProperty]>k__BackingField.

编译器脱糖添加了 < 和 >,因为开发人员代码将它们添加为私有字段名称是非法的。

这是一个示例,其中我试图找到名为 GlobalIgnores 的 属性 的私有支持字段,它没有 setter(意思与拥有私有 setter).

public class Configuration
{
    public Configuration()
    {
        GlobalIgnores = new List<string>{"IgnoredPropertyName"};
    }
    
    public IEnumerable<string> GlobalIgnores { get; }
}

protected IList<string> PropertyNamesToTempIgnore { get; } = new List<string>{"ReplacedIgnoredPropertyName"};

public void SetPrivateBackingFieldOfPropertyWithNoSetter()
{
    var configuration = new Configuration();
    var configurationType = configuration.GetType();
    const string globalIgnoresFieldName = "<GlobalIgnores>k__BackingField";
    var fieldInfo = configurationType.GetField(globalIgnoresFieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
    fieldInfo.SetValue(configuration, PropertyNamesToTempIgnore);
}

免责声明:除非绝对必要,否则我不建议使用它,因为这些自动生成的私有字段的命名并非 100% 在 C# 规范中指定,并且它可能会在不同的编译器实现中发生变化。