【问题标题】:Temporarily storing and re-setting a property value in C#在 C# 中临时存储和重新设置属性值
【发布时间】:2014-05-30 07:40:10
【问题描述】:

当我在特定代码库中编写测试时,我经常需要在我新添加的代码之前设置一个静态属性,并在离开时重新设置它。示例

public void SomeMethod(){
    val oldVal = Notifier.DoNotify;
    Notifier.DoNotify = false;

    // Some code...

    Notifier.DoNotify = oldVal;
}

我认为这种方式很难看,我想要更好的东西,这也使得忘记重新设置值变得更加困难。

我试着做点什么,得到了这个。我看到了一些好处,但这是一个非常有说服力的解决方案。

public class ValueHolder<T> : IDisposable
{
    private readonly Action<T> setter;
    private readonly T oldVal;

    public ValueHolder(Func<T> getter, T tempVal, Action<T> setter)
    {
        this.setter = setter;
        oldVal = getter();
        setter(tempVal);
    }

    public void Dispose()
    {
         setter(oldVal);
    }
}

然后像这样使用

public void SomeMethod(){
    using(new ValueHolder<bool>(() => Notifier.DoNotify, false, (b) => Notifier.DoNotify(b)) {

        // Do something ...
    }
}

有什么好的解决办法?

编辑删除了对测试的引用。似乎分散了我想问的内容)

【问题讨论】:

  • 你最好希望你的测试不要并行运行......
  • 也许codereview.stackexchange.com 更适合解决此类问题
  • 你使用什么测试框架?通常有一种方法可以在测试方法之前和之后运行一些代码。
  • 它看起来确实比手动重置值更好。不过,我会更改类的名称以专注于其重置行为。
  • 感谢 cmets!虽然我不是在寻找如何使用测试框架来解决这个问题。我精通单元测试,但现在对学习 C# 更感兴趣

标签: c#


【解决方案1】:

您可以做一些事情来使其更容易编写。我会尝试利用您正在使用的任何测试框架来帮助在测试之前和之后运行一些设置/清理代码。但除非这里有一些可以减少样板的技巧。

首先我们可以创建一个帮助类,它会减少创建ValueHolder&lt;T&gt; 实例的样板文件。我将使用 LINQ 表达式自动创建 getter 和 setter 委托,而不是要求调用者同时传入 getter 和 setter(尽管这在高级情况下仍然有用)。

public class ValueHolder
{
    public static ValueHolder<T> Create<T>(Expression<Func<T>> staticProp, T tempVal)
    {
        var ex = (MemberExpression)staticProp.Body;
        var prop = (PropertyInfo)ex.Member;
        var getter = (Func<T>)Delegate.CreateDelegate(typeof(Func<T>), prop.GetGetMethod());
        var setter = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), prop.GetSetMethod());
        return new ValueHolder<T>(getter, tempVal, setter);
    }
}

这让创作更加简洁

ValueHolder.Create(() => Notify.DoNotify, false);

其次,您可能需要设置其中的一些,而大量使用有点难看。我对这个问题的answer 提供了一种使用从List&lt;T&gt; 继承的类以更简单的方式处理这种情况的方法@ 也是IDisposable。结合这些,您可以使您的设置更加简单:

public void SomeTestMethod()
{
    // before any state has change.

    using (Setup())
    {
          // Test code.
    }

    // the cleanup code has run so the state is reset here.
}

private static IDisposable Setup()
{
    return new DisposableList() {
        ValueHolder.Create(() => Notify.DoNotify, false),
        ValueHolder.Create(() => ConnectionManager.MaxConnections, 100),
        ValueHolder.Create(() => SomeClass.SomeProp, 2.5),
        // etc., I think you get the idea.
    };
}

【讨论】:

  • 我喜欢使用列表的想法。使用 Linq 似乎有点贵?
  • @Peteter 表达式树和反射比 lambdas 更昂贵,但我怀疑这会花费大量时间。大约是数百纳秒而不是数十个。除非您对测试有一些非常严格的性能要求,否则您不会注意到这一点。
  • 谢谢!你也许是对的。 (性能始终很重要 - 我关心用户笔记本电脑的电池寿命。我已经删除了对测试的引用 :-) 尽管如此,即使是测试我也关心性能。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多