C# 警告:字段从未分配给,并且其默认值始终为空

C# 警告:字段从未分配给,并且其默认值始终为空,c#,compiler-construction,compiler-warnings,C#,Compiler Construction,Compiler Warnings,我得到以下信息: 警告:字段从未分配给,并且其默认值始终为空 我的代码看起来像(它被简化了,所以毫无用处): 这不是编译器中的错误 请。请看下面的答案 下面的解释不正确 显示该警告是因为在以下场景中,将使用未赋值的dbMyProp。1.MyClass被实例化。2.调用实例MyProp属性。(这将导致NullReferenceException) 还是我错过了什么 你错过了什么。在lambda表达式中,您创建了MyClass的新实例,并为其dbMyProp字段赋值。但是,您从未在实例方法中指定当

我得到以下信息:

警告:字段从未分配给,并且其默认值始终为空

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


这不是编译器中的错误

请。请看下面的答案

下面的解释不正确


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

还是我错过了什么

你错过了什么。在lambda表达式中,您创建了
MyClass
新实例,并为其
dbMyProp
字段赋值。但是,您从未在实例方法中指定当前实例的
dbMyProp
字段

请查看此示例:

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

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

var exp = MyClass.FromMyEntity.Compile();
var mc = exp(new MyEntity { MyProp = "abc"});
Console.WriteLine(mc.MyProp);
我尝试了一些技巧,比如在MyClass上使用私有构造函数,但警告仍然存在

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



如果有两个不同的元素名为
dbMyProp
,但是
MyEntity.MyProp
从未写入此处,则会使事情变得混乱

表达式不是代码。它是意图;因此,在编译器级别,确实可以说,没有代码实际分配该字段。有一个
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
      }
    );
}


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

正如您所说,您的代码被简化了,因此我们不一定能说出问题的原因。您也不包括发出警告的变量。请再次重新编辑-这一切都归结为您的原始代码是一个
表达式。
表达式
与通常的代码不同。我看到了这种差异。这是对其他答案的响应。“…从未分配给,并且其默认值始终为null”-这不是真的?在程序的编译时状态下,该语句是真的。您可以手动分配到
null
,然后看到警告消失。对于当前实例,这是正确的。只需编写以下代码,您就会看到它的真实性:
var myprop=new MyClass().myprop。我同意你的第一行,但我强烈不同意其余的;这是无关的。显示的C#代码不包含任何分配字段的C#代码。看起来是这样,但事实并非如此。@MarcGravel:是的,在阅读了OP的Update2之后,我同意你的看法。现在试着理解你的答案。好吧,你知道实例字段意味着为了访问它,你需要有一个对象的当前实例。因此,想象有人从外部创建MyClass的新实例,如:
var instance=new MyClass()。此实例的
dbMyProp
字段从未赋值。因为它是一个私有字段,所以唯一可以给它赋值的地方就是类的实例方法或者构造函数。请参阅我更新的示例。在这种情况下,编译器也应该生成一个警告,因为您从未为私有字段赋值。我猜编译器毕竟不是完美的:-)@DarinDimitrov我不同意;在编辑中,有一个明确的赋值(a
stfld
)给
dbMyProp
。在原始代码中没有:只有一个
表达式
。表达式不是常规代码。@MarcGravel,我完全同意你对封面下发生的事情的深入技术解释。谢谢你。但我是从开发人员/用户的角度来看的:在这两种情况下,作为类的消费者,如果我尝试使用新创建的类实例的
MyProp
,我会得到一个NRE。在第一种情况下,编译器会警告我,而在第二种情况下它不会。当然,作为这个类的开发人员,我可能会将构造函数标记为private,这样这个类的所有实例都是通过特定的静态方法创建的,该方法有效地设置了private字段;如果你拿走了所有的
MyProp
内容,但留下了
dbMyProp
赋值-它仍然认为
dbMyProp
未使用。我理解编译器无法通过反射来解析设置。但它可以在表达式树的生成过程中处理这个问题。对于这里的用户来说,静态函数(可以调用也可以不调用)之间有什么不同。@TN
表达式
不是函数;它从不被调用(不能被调用)-它只是一个描述您键入的某些代码的对象模型。这是关键的区别。通常,在运行时检查
表达式
,但这与实际执行它无关。执行它的唯一方法是在运行时编译
表达式
。可以调用函数,也可以编译表达式(例如,到IL,到SQL,…),然后也可以调用。(只是不要认为概率更低。)@TN表达式可以在运行时编译。构建时编译器不会试图知道您在运行时正在做什么。在构建时,完全正确的说法是:该字段从未分配。具体而言,没有任何
stfld
对其进行操作。你的“爱”可以是
var exp = MyClass.FromMyEntity.Compile();
var mc = exp(new MyEntity { MyProp = "abc"});
Console.WriteLine(mc.MyProp);
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
      }
    );
}
public static readonly Func<MyEntity, MyClass> FromMyEntity = e => new MyClass
{
    dbMyProp = e.MyProp // ...
};