开发者

A property or indexer may not be passed as an out or ref parameter

I'm getting the above error and unable to resolve it. I googled a bit but can't get rid of it.

Scenario:

I have class BudgetAllocate whose property is budget which is of double type.

In my dataAccessLayer,

In one of my classes I am trying to do this:

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

Which is throwing this error:

Property or indexer may not be passed as an out or ref parameter at compile t开发者_开发问答ime.

I even tried this:

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

Everything else is working fine and references between layers are present.


Others have given you the solution, but as to why this is necessary: a property is just syntactic sugar for a method.

For example, when you declare a property called Name with a getter and setter, under the hood the compiler actually generates methods called get_Name() and set_Name(value). Then, when you read from and write to this property, the compiler translates these operations into calls to those generated methods.

When you consider this, it becomes obvious why you can't pass a property as an output parameter - you would actually be passing a reference to a method, rather than a reference to an object a variable, which is what an output parameter expects.

A similar case exists for indexers.


This is a case of a leaky abstraction. A property is actually a method, the get and set accessors for an indexer get compiled to get_Index() and set_Index methods. The compiler does a terrific job hiding that fact, it automatically translates an assignment to a property to the corresponding set_Xxx() method for example.

But this goes belly up when you pass a method parameter by reference. That requires the JIT compiler to pass a pointer to the memory location of the passed argument. Problem is, there isn't one, assigning the value of a property requires calling the setter method. The called method cannot tell the difference between a passed variable vs a passed property and can thus not know whether a method call is required.

Notable is that this actually works in VB.NET. For example:

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

The VB.NET compiler solves this by automatically generating this code for the Run method, expressed in C#:

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

Which is the workaround you can use as well. Not quite sure why the C# team didn't use the same approach. Possibly because they didn't want to hide the potentially expensive getter and setter calls. Or the completely undiagnosable behavior you'll get when the setter has side-effects that change the property value, they'll disappear after the assignment. Classic difference between C# and VB.NET, C# is "no surprises", VB.NET is "make it work if you can".


you cannot use

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

replace bd.Budget with some variable.

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


Possibly of interest - you could write your own:

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


Place the out parameter into a local variable and then set the variable into bd.Budget:

double tempVar = 0.0;

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

Update: Straight from MSDN:

Properties are not variables and therefore cannot be passed as out parameters.


This is a very old post, but I'm ammending the accepted, because there is an even more convienient way of doing this which I didn't know.

It's called inline declaration and might have always been available (as in using statements) or it might have been added with C#6.0 or C#7.0 for such cases, not sure, but works like a charm anyway:

Inetad of this

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

use this:

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


So Budget is a property, correct?

Rather first set it to a local variable, and then set the property value to that.

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


Usually when I'm trying to do this it's because I want to set my property or leave it at the default value. With the help of this answer and dynamic types we can easily create a string extension method to keep it one lined and simple.

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

Use like so;

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

Optional

On a side note, if that's an SQLDataReader you may also make use of GetSafeString extension(s) to avoid null exceptions from the reader.

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

Use like so;

bd.Budget = objReader.GetSafeString(i).ParseAny(typeof(double));
bd.Budget = objReader.GetSafeString("ColumnName").ParseAny(typeof(double));


I had the same problem (5 minutes ago) and I solved it using old style properties with getter and setter, whose use variables. My code:

public List<int> bigField = new List<int>();
public List<int> BigField { get { return bigField; } set { bigField = value; } }

So, I just used bigField variable. I'm not the programmer, if I misunderstood the question, I'm really sorry.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜