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我不同意;在编辑中,有一个明确的赋值(astfld
)给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 // ...
};