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

C# 属性或索引器不能作为out或ref参数传递,c#,.net,error-handling,C#,.net,Error Handling,我收到上述错误,无法解决。 我在谷歌上搜索了一下,但还是无法摆脱它 情景: 我有一个BudgetLocate类,它的属性是budget,它是双重类型 在我的dataAccessLayer中 在我的一门课上,我试图做到这一点: double.TryParse(objReader[i].ToString(), out bd.Budget); 这会引发以下错误: 属性或索引器不能在处作为out或ref参数传递 编译时 我甚至试过: double.TryParse(objReader[i].ToStr

我收到上述错误,无法解决。 我在谷歌上搜索了一下,但还是无法摆脱它

情景:

我有一个BudgetLocate类,它的属性是budget,它是双重类型

在我的dataAccessLayer中

在我的一门课上,我试图做到这一点:

double.TryParse(objReader[i].ToString(), out bd.Budget);
这会引发以下错误:

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

我甚至试过:

double.TryParse(objReader[i].ToString().Equals(DBNull.Value) ? "" : objReader[i].ToString(), out bd.Budget);
其他一切正常,层之间存在引用。

您不能使用

double.TryParse(objReader[i].ToString(), out bd.Budget); 
用一些变量替换bd.Budget

double k;
double.TryParse(objReader[i].ToString(), out k); 

所以预算是一种财产,对吗

而是首先将其设置为局部变量,然后将属性值设置为该值

double t = 0;
double.TryParse(objReader[i].ToString(), out t); 
bd.Budget = t;

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

double tempVar = 0.0;

if (double.TryParse(objReader[i].ToString(), out tempVar))
{
    bd.Budget = tempVar;
}
更新:直接从MSDN:

属性不是变量和变量 因此,不能将其作为输出 参数


其他人已经给出了解决方案,但关于为什么这是必要的:属性只是方法的语法糖

例如,当您使用getter和setter声明名为
Name
的属性时,编译器实际上会在后台生成名为
get\u Name()
set\u Name(value)
的方法。然后,当您读取和写入此属性时,编译器将这些操作转换为对生成的方法的调用

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


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

这是一个泄漏抽象的情况。属性实际上是一个方法,索引器的get和set访问器被编译为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
VB.NET编译器通过自动为Run方法生成以下代码(用C#表示)来解决此问题:


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

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

    //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;
}
//double.TryParse(,out bd.Budget);
bool result=TryParse(s,value=>bd.Budget=value);
}
public bool TryParse(字符串s,Action setValue)
{
双重价值;
var结果=double.TryParse(s,输出值);
如果(结果)设定值(值);
返回结果;
}

这是一篇非常古老的帖子,但我正在修改已被接受的帖子,因为有一种更方便的方式可以做到这一点,我不知道

它被称为内联声明,可能一直可用(如在使用语句时),也可能在这种情况下添加了C#6.0或C#7.0,但不确定,但无论如何都很有用:

这一切

double temp;
double.TryParse(objReader[i].ToString(), out temp);
bd.Budget = temp;
使用以下命令:

double.TryParse(objReader[i].ToString(), out double temp);
bd.Budget = temp;

通常,当我尝试这样做时,是因为我想设置我的属性或将其保留为默认值。借助和
dynamic
类型,我们可以轻松创建一个字符串扩展方法,使其保持一行且简单

public static dynamic ParseAny(this string text, Type type)
{
     var converter = TypeDescriptor.GetConverter(type);
     if (converter != null && converter.IsValid(text))
          return converter.ConvertFromString(text);
     else
          return Activator.CreateInstance(type);
}
像这样使用

bd.Budget = objReader[i].ToString().ParseAny(typeof(double));

// Examples
int intTest = "1234".ParseAny(typeof(int)); // Result: 1234
double doubleTest = "12.34".ParseAny(typeof(double)); // Result: 12.34
decimal pass = "12.34".ParseAny(typeof(decimal)); // Result: 12.34
decimal fail = "abc".ParseAny(typeof(decimal)); // Result: 0
string nullStr = null;
decimal failedNull = nullStr.ParseAny(typeof(decimal)); // Result: 0
bd.Budget = objReader.GetSafeString(i).ParseAny(typeof(double));
bd.Budget = objReader.GetSafeString("ColumnName").ParseAny(typeof(double));
可选

另一方面,如果这是
SQLDataReader
,您还可以使用
GetSafeString
扩展名来避免读取器出现空异常

public static string GetSafeString(this SqlDataReader reader, int colIndex)
{
     if (!reader.IsDBNull(colIndex))
          return reader.GetString(colIndex);
     return string.Empty;
}

public static string GetSafeString(this SqlDataReader reader, string colName)
{
     int colIndex = reader.GetOrdinal(colName);
     if (!reader.IsDBNull(colIndex))
          return reader.GetString(colIndex);
     return string.Empty;
}
像这样使用

bd.Budget = objReader[i].ToString().ParseAny(typeof(double));

// Examples
int intTest = "1234".ParseAny(typeof(int)); // Result: 1234
double doubleTest = "12.34".ParseAny(typeof(double)); // Result: 12.34
decimal pass = "12.34".ParseAny(typeof(decimal)); // Result: 12.34
decimal fail = "abc".ParseAny(typeof(decimal)); // Result: 0
string nullStr = null;
decimal failedNull = nullStr.ParseAny(typeof(decimal)); // Result: 0
bd.Budget = objReader.GetSafeString(i).ParseAny(typeof(double));
bd.Budget = objReader.GetSafeString("ColumnName").ParseAny(typeof(double));

在bd.Budget中,bd是BudgetLocate类的对象。抱歉,我忘了。可能重复的可能重复的可能重复的刚刚发现的可能重复的正在使用已定义了字段的用户类型,我希望从中填充
DataGrid
,然后来了解它,仅使用属性自动。切换到属性会破坏我在字段中使用的一些ref参数。必须定义局部变量才能进行解析。谢谢。但是我可以知道为什么吗?为什么要使用一个额外的变量???@pratik您不能将属性作为输出参数传入,因为无法保证属性实际上有一个setter,所以您需要额外的变量。@mjd79:您的推理不正确。编译器知道是否有setter。假设有一个二传手;应该允许吗?@dhinesh,我认为OP正在寻找他为什么不能做这件事的答案,而不仅仅是他必须做什么。阅读Hans Passant的答案和Eric Lippert的评论。@dhhinesh他不能这么做的“真正”原因是因为他使用的是C#而不是VB,而VB确实允许这样做。我来自VB世界(显然是?),我经常对C#施加的额外限制感到惊讶。你的推理直到最后一点都是正确的。out参数所需的是对变量的引用,而不是对对象的引用。@EricLippert但变量不也是对象,或者我缺少什么?@meJustAndrew:变量绝对不是对象。变量是一个存储位置。存储位置包含(1)对引用类型(或null)的对象的引用,或