【问题标题】:Warning: Field is never assigned to, and will always have its default value null警告:字段永远不会分配给,并且将始终具有其默认值 null
【发布时间】:2012-09-21 09:05:17
【问题描述】:

我收到以下消息:

警告:字段永远不会分配给,并且始终具有其默认值 null。

我的代码看起来像(它被简化了,所以没用):

public class MyEntity
{
    // ...
    public string MyProp { get; set; }
}

public class MyClass
{
    string dbMyProp;

    public string MyProp { get { return dbMyProp.Replace("a", "b"); } }

    public static readonly Expression<Func<MyEntity, MyClass>> FromMyEntity = e => new MyClass
    {
        dbMyProp = e.MyProp // ...
    };
}

我认为消息不正确。

这是 C# 编译器中的错误还是我遗漏了什么?

更新字段是dbMyProp。它已简化,但仍会产生此警告。

UPDATE2以下代码不会产生这样的警告:

public class MyClass2
{
    string dbMyProp;

    public string MyProp { get { return dbMyProp.Replace("a", "b"); } }

    public static MyClass2 FromMyEntity(MyEntity e)
    {
        return new MyClass2 
        {
            dbMyProp = e.MyProp // ...
        };
    }
}

【问题讨论】:

  • 如您所说,您的代码已简化,因此我们不一定能说出问题的原因。您也不包括针对哪个变量引发警告。
  • 再次重新编辑 - 这一切都归结为您的原始代码是 ExpressionExpression 与通常的代码不同
  • 我看到了差异。这是对其他答案的回应。

标签: c# compiler-construction compiler-warnings


【解决方案1】:

Expression不是代码。这是意图;所以在 编译器 级别,确实可以说没有代码实际上曾经分配过该字段。有一个MemberAssignment(来自Expression.Bind),但这无关紧要。

如果表达式被编译,那只会通过实际的字段分配。这将是运行时的反射,编译器不会尝试检测。

这是实际上在编译时的情况:

static MyClass()
{
    ParameterExpression CS$0$0000;
    FromMyEntity = Expression.Lambda<Func<MyEntity, MyClass>>(
      Expression.MemberInit(
        Expression.New(
          (ConstructorInfo)methodof(MyClass..ctor),
          new Expression[0]
        ),
        new MemberBinding[] {
          Expression.Bind(
            fieldof(MyClass.dbMyProp),
            Expression.Property(
              CS$0$0000 = Expression.Parameter(typeof(MyEntity), "e"),
              (MethodInfo)methodof(MyEntity.get_MyProp)
            )
          )
        }
      ),
      new ParameterExpression[] {
        CS$0$0000
      }
    );
}

注意什么不是对一个字段有任何赋值;只需使用虚构的 fieldof 运算符(意​​思是:它直接嵌入字段句柄 - 它不使用名称的 string)。

按照同样的逻辑,如果我们将其设为委托,警告就会消失

public static readonly Func<MyEntity, MyClass> FromMyEntity = e => new MyClass
{
    dbMyProp = e.MyProp // ...
};

现在这个代码;它不仅仅是一个对象模型,它显示“这是我做的,如果我被编译了”。

【讨论】:

  • 我了解编译器无法通过反射解析设置。但它可以在表达式树的生成过程中处理它。对于用户而言,可能会或可能不会调用的静态函数有什么不同。
  • @TN an Expression 不是函数;它从不被调用(它不能被调用/调用) - 它只是一个描述一些对象模型您键入的代码。这是关键的区别。通常,您在运行时检查 Expression,但这与实际执行它无关。执行它的唯一方法是在运行时编译Expression
  • 函数可以被调用并且表达式可以被编译(例如到IL,到SQL,...)然后也可以被调用。 (只是不要认为概率较低。)
  • @TN 可以在运行时编译表达式。构建时编译器不会尝试知道您在运行时正在做什么。在构建时,完全正确的说法是:永远不会分配该字段。具体来说,没有stfld 对其进行操作。您的“可以编译”与说 string“可以编译”(通过CSharpCodeProvider)没有什么不同。完全准确,但与问题完全无关。
  • @TN 实际上,不:大多数表达式都没有编译;它们由诸如 ORM 或 RPC 堆栈之类的工具处理,这些工具读取树并制作自己的自己的 IL 以实现类似的结果,但通过它们自己的内部 goo。如果用户只是要编译它,那么坦率地说,他们一开始就不应该使用Expression
【解决方案2】:

是的,这似乎是编译器的一个缺点。它显然无法看穿(不包括)表达式。
以下按预期打印“bbc”:

var exp = MyClass.FromMyEntity.Compile();
var mc = exp(new MyEntity { MyProp = "abc"});
Console.WriteLine(mc.MyProp);

我尝试了一些技巧,例如在 MyClass 上使用私有构造函数,但警告仍然存在。

似乎编译器应用了更保守的算法,只检查是否有任何(正常)成员分配给 dbMyProp


你有两个不同的元素名称为dbMyProp,但MyEntity.MyProp 永远不会写在这里。

【讨论】:

  • 那是无关的;如果你把所有 MyProp 的东西都拿走,但留下 dbMyProp 分配 - 它仍然认为 dbMyProp 未使用。
【解决方案3】:

或者我错过了什么?

你错过了什么。在 lambda 表达式中,您创建了 MyClassnew 实例并分配了其 dbMyProp 字段。但是您从未在实例方法中分配 current 实例的 dbMyProp 字段。

看看这个例子:

var instance = new MyClass();
var myProp = instance.MyProp; // BOOM - you never assigned a value to the dbMyProp field

【讨论】:

  • 好吧,您知道实例字段意味着要访问它,您需要拥有该对象的当前实例。所以想象一下有人从外部创建了一个新的 MyClass 实例,例如:var instance = new MyClass();。此实例的 dbMyProp 字段永远不会被赋值。而且由于它是一个私有字段,因此您可以为其赋值的唯一地方是类的实例方法或构造函数。请参阅我更新的示例。
  • 编译器在这种情况下也应该产生警告,因为您从不为私有字段赋值。我猜编译器毕竟不是完美的:-)
  • @DarinDimitrov 我不同意;在编辑中,有一个明确的分配(stfld)到dbMyProp。在原始代码中,没有:只有一个Expression。表达式不是常规代码。
  • @MarcGravell,我完全同意您对幕后情况的深入技术解释。谢谢你。但我是从开发人员/用户的角度来看的:在这两种情况下,作为类的消费者,如果我尝试使用新创建的类实例的MyProp,我都会得到 NRE。在第一种情况下,编译器会警告我,在第二种情况下不会。当然,作为此类的开发人员,我可能会将构造函数标记为私有,以便此类的所有实例都是通过有效设置私有字段的特定静态方法创建的。
【解决方案4】:

这不是编译器中的错误。

请。查看Marc Gravell的答案

以下解释不正确。


显示警告是因为在下面的场景中将使用dbMyProp 的未分配值。 1. MyClass 被实例化。 2.使用实例MyProp属性被调用。 (这将导致NullReferenceException

【讨论】:

  • "...从未分配给,并且总是将其默认值为 null" - 这不是真的吗?
  • 程序在编译时状态下该语句为真。您可以手动分配给null,然后看到警告消失了。
  • 当前实例是这样。只需编写以下代码,您就会看到它的真实性:var myprop = new MyClass().MyProp;.
  • 我同意你的第一行,但我强烈不同意其余的;那是无关的。显示的 C#包含任何分配该字段的 C# 代码。看起来是这样,但实际上不是。
  • @MarcGravell:是的,在阅读了 OP 的 Update2 之后,我同意你的看法。现在试图理解你的答案。
猜你喜欢
  • 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
相关资源
最近更新 更多