为什么Resharper不能告诉初始化

时间:2018-03-14 12:41:35

标签: c# unit-testing resharper

我正在编写单元测试,而我偶然发现了Resharper的建议。

  

任何执行路径都不使用赋值。

在以下代码snipet中。

[Test]
[TestCase((int)OddsRoundingModes.Floor)]
public void GetBaseOddsRoundingMode_WithCorrectRoundingMode_ShouldReturnCorrectRoundingMode(int oddsRoundingMode)
{
    // Arrange
    var oddsRoundingModeStr = oddsRoundingMode.ToString(); // <-- suggestion here
    var mock = new Mock<IConstantsStorage>();
    var oddsRoundingConfiguration = new OddsRoundingConfiguration(mock.Object);
    mock.Setup(h => h.TryGetConstant(It.IsAny<string>(), It.IsAny<int>(), out oddsRoundingModeStr))
        .Returns(true);

    // Act
    var roundingMode = oddsRoundingConfiguration.GetBaseOddsRoundingMode(0);

    // Assert
    Assert.AreNotEqual(roundingMode, OddsRoundingModes.None);
}

但是当我将此更改为未在声明时初始化时,模拟未正确设置且测试失败,因为oddsRoundingModeStr未初始化且模拟将其返回为null。

为什么Resharper看不到这个?

编辑:

public bool TryGetConstant(string name, int siteId, out string value)
{
    value = RetrieveConstant(_constantsModel, name, siteId);

    return value != null;
}

private string RetrieveConstant<T>(IConstantsModel<T> model, string constName, int siteId)
    where T : IConstant, new()
{
    if (model.Constants.TryGetValue(constName, out List<T> values))
    {
        var constant = values.FirstOrDefault(v => v.Name == constName && v.SiteIds.Contains(siteId));
        if (constant != null)
        {
            return constant.ConstantValue;
        }
    }

    return null;
}

2 个答案:

答案 0 :(得分:3)

遵循正常的C#语义,将该变量初始化为的值无关紧要,因为out在为其分配新值之前无法读取数据。因此,resharper通知是合适的。

我看到使用此代码可以实现非标准语义的几种方法:

  1. out是CLR级别的装饰ref。因此,低级代码可将其视为等同于ref

    void Main()
    {
        Ref r = R;
        Out o = (Out)Delegate.CreateDelegate(typeof(Out), null, r.Method);
        int i = 2;
        o(out i);
        i.Dump();
    }
    
    delegate void Out(out int x);
    delegate void Ref(ref int x);
    
    void R(ref int x)
    {
        x++;
    }
    
  2. Setup接受委托,然后对闭包对象使用私有反射。

  3. Setup使用Expression<T>,即lambda的语法树,并以非标准方式解释表达式。

    在此上下文中,lambda表达式不是要执行的C#代码,而是基本上描述如何设置模拟的DSL

  4. 选项3似乎最有可能

答案 1 :(得分:2)

Setup接受表达式树 - 并且Moq分析该表达式树以创建moq。在这种情况下,您基本上是说Moq应该创建IConstantsModel的实现,它接受任何字符串,任何int,返回true并返回您在oddsRoundingModeStr中提供的out参数值。因此,在分析此表达式树时,Moq将提取oddsRoundingModeStr的实际值(它被捕获并存储在编译器生成的类的字段中)并且实际上将使用它。 Resharper只是无法实现这一点,因此像往常一样提供警告。

如何从表达式树中提取out变量值的小例子:

class Program {
    static void Main(string[] args) {
        int result = 2; // gives warning from your question
        var back = ExtractOutValue(s => int.TryParse(s, out result));           
        Debug.Assert(back == result);
    }

    static int ExtractOutValue(Expression<Action<string>> exp) {
        var call = (MethodCallExpression)exp.Body;            
        var arg = (MemberExpression) call.Arguments[1];
        return (int) ((FieldInfo)arg.Member).GetValue(((ConstantExpression)arg.Expression).Value);            
    }        
}