通过将静态字段作为方法参数传递来更新静态字段

Updating a static field by passing it as a method argument

提问人:user999666 提问时间:9/19/2023 最后编辑:user999666 更新时间:9/19/2023 访问量:71

问:

我的应用程序将涉及对静态字段的大量修改。我想知道如何通过将不同类的静态字段作为方法参数传递给实用程序 setter 方法来更新它们。

internal static class Hero1
{
  public static bool awesomeAbility = false;
  public static bool anotherAwesomeAbility = false;
}

internal static class Hero2
{
  public static bool awesomeAbility = false;
  public static bool anotherAwesomeAbility = false;
}

internal static class Utils
{
  public static async void ToggleAbility(ability, bool value)
  {
    Logger.Write($@"{ability} = {value}");
    ability = value;
  }
}

// example usage....
ToggleAbility(Hero1.awesomeAbility, true);
ToggleAbility(Hero2.anotherAwesomeAbility, false);
C# 静态方法 帮助程序

评论

1赞 Sweeper 9/19/2023
虽然这是可能的,但我不认为“大量修改静态场”首先不是一个好的设计。
0赞 user999666 9/19/2023
我知道这一点,但这是一个已经有很多这样的现有项目。我试图通过清理每个能力的所有冗余切换方法并将它们放入一个可以接收该能力作为方法参数来简化它的一个方面。我之所以将其描述为异步方法,是因为通常根据动态毫秒数将能力设置回其原始状态。对我来说,目前的障碍只是弄清楚如何将静态字段传递和修改到这个实用方法。
0赞 lidqy 9/19/2023
如果将静态字段设为公共字段,则几乎可以从引用程序集;)的任何代码段更新该字段通常的 OOP 方式是在类本身上有一个方法或属性,用于控制如何、由谁、何时等更新。他们称之为封装。它是对象编程的核心原则之一。但尽管如此,你仍然可以用任何你喜欢的范式进行编码。
0赞 user999666 9/19/2023
@lidqy如何定义方法参数以接受任何类型的静态字段?
0赞 lidqy 9/19/2023
如果要确保字段本身已更新并避免“按值”复制,则需要在方法参数上使用“ref”关键字通过引用传递它。如果并发访问/争用条件是一个问题,则需要实现某种锁定/同步。

答:

1赞 lidqy 9/19/2023 #1

嗨,您需要关键字才能实际更改所属类(静态或非静态)中的字段值。否则,您只需更改副本,该副本在调用方法后就会被转移。ref

但是,它不能再是异步的。

internal static class Utils
{
  public static void ToggleAbility(ref bool ability, bool value)
  {
    Logger.Write($@"{ability} = {value}");
    ability = value;
  }
}

ToggleAbility(ref Hero1.awesomeAbility, true);
ToggleAbility(ref Hero2.anotherAwesomeAbility, false);
1赞 Sweeper 9/19/2023 #2

如果允许调用方传递表达式树 (),则可以执行此操作,然后可以将表达式树引用的字段作为 .Expression<Func<bool>>FieldInfo

async void ToggleAbility(Expression<Func<bool>> field, bool value) {
    if (field.Body is MemberExpression memberExpression &&
        memberExpression.Member is FieldInfo fieldInfo) {
        
        // instead of field.Body, you can also use fieldInfo.Name for a simple name
        Logger.Write($@"{field.Body} = {fieldInfo.GetValue(null)}");

        // this sets the field's value
        fieldInfo.SetValue(null, value);

        // if you want to set it back asynchronously some time later...
        // await Task.Delay(...);
        // fieldInfo.SetValue(null, !value);
    } else {
        // the expression tree is not an access to a public static field
        // you could e.g. throw an exception 
    }
}

用法:

ToggleAbility(() => Hero1.awesomeAbility, true);

设置字段不是很快。如果这对您来说是个问题,您可以选择“现场设置器”SetValueAction<bool>

async void ToggleAbility(Action<bool> setter, bool value) {
    setter(value)
    await Task.Delay(...);
    setter(!vakue)
}
ToggleAbility(x => Hero1.awesomeAbility = x, true);

但是,您无法使用此方法访问字段名称或其当前值,除非您还将它们作为其他参数传入:ToggleAbility

ToggleAbility(
    x => Hero1.awesomeAbility = x, 
    true,
    nameof(Hero1.awesomeAbility),
    Hero1.awesomeAbility);

如果你的方法不需要是异步的,你也可以将该字段作为参数传入,如 lidqy 的答案所示。ref

评论

0赞 lidqy 9/19/2023
这真是太棒了,因为它非常慢,并且需要大量代码才能完成简单的任务,以便为字段赋值;)
0赞 lidqy 9/19/2023
怎么样:调用为:async void ToggleAbility(Action<bool> setter, bool value) => setter(value);await ToggleAbility( val => Hero1.awesomeAbility = val, true);
0赞 lidqy 9/19/2023
最后,为什么不直接做:?Hero1.awesomeAbility = true
0赞 Sweeper 9/19/2023
@lidqy OP 似乎正在将一些通用逻辑提取到此方法中 - 它不仅仅是设置字段。采取确实有效 - 我个人不喜欢一直重写。也许我应该把它作为替代方案加进去。ToggleAbilityAction<bool>= val
0赞 Sweeper 9/19/2023
@lidqy 实际上,仅仅取一个是行不通的,因为 OP 想要访问方法中字段的名称和值。Action