属性或索引器不能作为out或ref参数传递

Pra*_*tik 73 .net c# error-handling

我收到上述错误,无法解决.我google了一下,但无法摆脱它.

场景:

我有类BudgetAllocate,其属性是预算,是双重类型.

在我的dataAccessLayer中,

在我的一个课程中,我试图这样做:

double.TryParse(objReader[i].ToString(), out bd.Budget);
Run Code Online (Sandbox Code Playgroud)

这引发了这个错误:

在编译时,属性或索引器不能作为out或ref参数传递.

我甚至试过这个:

double.TryParse(objReader[i].ToString().Equals(DBNull.Value) ? "" : objReader[i].ToString(), out bd.Budget);
Run Code Online (Sandbox Code Playgroud)

其他一切工作正常,层之间的引用存在.

Mik*_*ain 128

其他人给了你解决方案,但至于为什么这是必要的:属性只是方法的语法糖.

例如,当您声明一个Name使用getter和setter 调用的属性时,编译器实际上会生成名为get_Name()and的方法set_Name(value).然后,当您读取和写入此属性时,编译器会将这些操作转换为对这些生成方法的调用.

当您考虑到这一点时,很明显为什么您不能将属性作为输出参数传递 - 您实际上将传递对方法的引用,而不是对变量的对象的引用,这是输出参数所期望的.

索引器也存在类似的情况.

  • 你的推理是正确的,直到最后一位.out参数期望引用*变量*,而不是*object*. (17认同)
  • @meJustAndrew:**变量绝对不是对象**。变量是“存储位置”。存储位置*包含(1)对引用类型的对象的引用(或为null),或(2)值类型的对象的值。不要混淆容器中包含的东西。 (3认同)
  • @meJustAndrew:考虑一个对象,例如一所房子。考虑一张写有房子地址的纸。考虑一个包含那张纸的抽屉。**抽屉和纸都不是房子**。 (3认同)

Han*_*ant 58

这是一个漏洞抽象的案例.属性实际上是一个方法,索引器的getset访问器被编译为get_Index()和set_Index方法.编译器做了一个很好的工作,隐藏了这个事实,它自动将一个属性的赋值转换为相应的set_Xxx()方法.

但是当您通过引用传递方法参数时,这会变得很糟糕.这需要JIT编译器将指针传递给传递参数的内存位置.问题是,没有一个,分配属性的值需要调用setter方法.被调用的方法无法区分传递的变量与传递的属性之间的区别,因此无法知道是否需要进行方法调用.

值得注意的是,这实际上适用于VB.NET.例如:

Class Example
    Public Property Prop As Integer

    Public Sub Test(ByRef arg As Integer)
        arg = 42
    End Sub

    Public Sub Run()
        Test(Prop)   '' No problem
    End Sub
End Class
Run Code Online (Sandbox Code Playgroud)

VB.NET编译器通过自动生成Run方法的代码来解决这个问题,用C#表示:

int temp = Prop;
Test(ref temp);
Prop = temp;
Run Code Online (Sandbox Code Playgroud)

您也可以使用哪种解决方法.不太清楚C#团队为什么不使用相同的方法.可能是因为他们不想隐藏潜在的昂贵的getter和setter调用.或者当setter具有改变属性值的副作用时,你将获得完全不可识别的行为,它们将在赋值后消失.C#和VB.NET之间的经典区别,C#是"毫无意外",VB.NET是"让它工作,如果你能".

  • 你不想生成昂贵的电话是正确的.第二个原因是copy-in-copy-out语义具有与引用语义不同的语义,并且对于ref传递具有两个微妙不同的语义是不一致的.(也就是说,遗憾的是,在某些罕见的情况下,编译的表达式树会进行复制复制.) (14认同)
  • 真正需要的是更多种类的参数传递模式,因此编译器可以在适当的地方替换"复制/复制",但在不存在的情况下会发出尖叫声. (2认同)

dhi*_*esh 30

你不能用

double.TryParse(objReader[i].ToString(), out bd.Budget); 
Run Code Online (Sandbox Code Playgroud)

用一些变量替换bd.Budget.

double k;
double.TryParse(objReader[i].ToString(), out k); 
Run Code Online (Sandbox Code Playgroud)

  • @ mjd79:你的推理不正确.编译器知道是否有setter.假设有一个二传手; 它应该被允许吗? (20认同)
  • @dhinesh,我认为OP正在寻找**为什么**他不能做到的答案,而不仅仅是他必须做的事情.阅读Hans Passant的回答和Eric Lippert的评论. (17认同)
  • 为什么要使用一个额外的变量? (9认同)
  • @pratik你不能将属性作为out参数传递,因为不能保证属性实际上有一个setter,所以你需要额外的变量. (5认同)
  • @dhinesh之所以不能做到这一点的“真正”原因是因为他使用的是C#而不是VB,它允许这样做。我来自VB世界(很明显吗?),我常常对C#施加的额外限制感到惊讶。 (2认同)

Ada*_*rth 9

将out参数放入局部变量,然后将变量设置为bd.Budget:

double tempVar = 0.0;

if (double.TryParse(objReader[i].ToString(), out tempVar))
{
    bd.Budget = tempVar;
}
Run Code Online (Sandbox Code Playgroud)

更新:直接来自MSDN:

属性不是变量,因此不能作为out参数传递.

http://msdn.microsoft.com/en-us/library/t3c3bfhx(v=vs.80).aspx


Dav*_*ead 8

可能有兴趣 - 你可以写自己的:

    //double.TryParse(, out bd.Budget);
    bool result = TryParse(s, value => bd.Budget = value);
}

public bool TryParse(string s, Action<double> setValue)
{
    double value;
    var result =  double.TryParse(s, out value);
    if (result) setValue(value);
    return result;
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*Dan 5

这是一个非常古老的帖子,但我正在修改已接受的帖子,因为有一种我不知道的更方便的方法。

它被称为内联声明并且可能一直可用(如在 using 语句中),或者它可能已在 C#6.0 或 C#7.0 中添加用于这种情况,不确定,但无论如何都像魅力一样:

这个

double temp;
double.TryParse(objReader[i].ToString(), out temp);
bd.Budget = temp;
Run Code Online (Sandbox Code Playgroud)

用这个:

double.TryParse(objReader[i].ToString(), out double temp);
bd.Budget = temp;
Run Code Online (Sandbox Code Playgroud)

  • 如果输入无效,我将使用返回来检查解析是否成功。 (2认同)