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

时间:2010-12-23 13:00:19

标签: c# .net error-handling

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

情景:

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

在我的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);

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

8 个答案:

答案 0 :(得分:113)

其他人已经为您提供了解决方案,但至于为什么这是必要的:属性只是方法的语法糖。

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

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

索引器存在类似的情况。

答案 1 :(得分:49)

这是一个漏洞抽象的案例。属性实际上是一种方法,索引器的 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#表示:

int temp = Prop;
Test(ref temp);
Prop = temp;

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

答案 2 :(得分:26)

你不能使用

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

用某个变量替换bd.Budget。

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

答案 3 :(得分:8)

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

double tempVar = 0.0;

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

更新:直接来自MSDN:

  

属性不是变量和   因此不能被传递出去   参数。

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

答案 4 :(得分:6)

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

    //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;
}

答案 5 :(得分:3)

这是一篇非常古老的文章,但是我要修改被接受的内容,因为有一种我不知道的更简便的方法。

它称为内联声明,可能一直可用(如使用语句),或者在某些情况下可能已经与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;

答案 6 :(得分:1)

所以预算是财产,对吗?

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

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

答案 7 :(得分:0)

通常,当我尝试执行此操作时,这是因为我想设置属性或将其保留为默认值。在this answerdynamic类型的帮助下,我们可以轻松地创建一个字符串扩展方法,以使其保持一致和简单。

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

可选

顺便说一句,如果是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.GetSafeString(i).ParseAny(typeof(double));
bd.Budget = objReader.GetSafeString("ColumnName").ParseAny(typeof(double));