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

10

122

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 time.

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.

Impress answered 23/12, 2010 at 13:0 Comment(5)
In bd.Budget bd is an object of class BudgetAllocate. Sorry I forgot.Impress
possible duplicate of C# property and ref parameter, why no sugar?Bowler
possible duplicate of Accessing properties through Generic type parameterMesdames
Possible duplicate of Passing properties by reference in C#Hussite
Just discovered this working with a user type that had fields defined which I was expected a DataGrid to populate from then come to learn it only autos with properties. Switching to properties broke some ref parameters I was using on my fields. Gotta define local variables to do the parsing with.Mcalpine
N
47

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); 
bd.Budget = k;
Neckcloth answered 23/12, 2010 at 13:2 Comment(7)
why to use one extra variable ??Impress
@pratik: the out modifier should not be initialized previously.Neckcloth
@pratik You can't pass in a property as an out parameter because there's no guarantee that the property actually has a setter, so you need the extra variable.Fogged
@mjd79: Your reasoning is incorrect. The compiler knows whether there is a setter or not. Suppose there was a setter; should it be allowed?Dolomite
Hmm - I would still think no. The out keyword defines a parameter that is passed by reference, and upon completion of the call, the value at that reference is set. Since properties in C# are (basically) wrappers around method calls, and not the actual underlying fields, you wouldn't be able to set the value of the property. Is that right?Fogged
@dhinesh, i think the OP is looking for an answer of why he can't do it, not just what he must do instead. Read the answer from Hans Passant and the comments from Eric Lippert.Vespiary
@Neckcloth The "real" reason why he can't do it is because he's using C# rather than VB which DOES allow this. I'm from the VB world (obviously?) and I'm often surprised at the extra restrictions C# imposes.Corrinnecorrival
I
196

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.

Incognizant answered 23/12, 2010 at 13:11 Comment(5)
@meJustAndrew: A variable absolutely is not an object. A variable is a storage location. A storage location contains either (1) a reference to an object of reference type (or null), or (2) the value of an object of value type. Don't confuse the container for the thing contained in it.Dolomite
@meJustAndrew: Consider an object, say, a house. Consider a piece of paper that has written on it the address of the house. Consider a drawer that contains that piece of paper. Neither the drawer nor the paper are the house.Dolomite
@MichaelS: There are a number of inaccuracies in your comment. For example: C# defines a variable as a storage location, the variable is neither the name associated with that location -- if any -- nor is it the data stored in that location. It's the location. Objects are not "just collections of variables" in C#. And so on.Dolomite
@MichaelS: As a member of the committee which standardizes the C# language I am familiar with the location of the specification, but thanks for the link. I think that maybe I should be the arbiter of what I intended the elements of my analogy to mean, but please do feel free to propose a novel analogy you like better, and then we can all learn from it.Dolomite
@EricLippert: Your analogy was quite clear, but doesn't represent actual programming very well. My analogy is quite standard, and accurate to how the computer actually works (sans my original omission of instruction data). Having not seen the original comment to which you replied, I will presume your comment made more sense in context and leave it at that.Necropolis
I
81

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".

Ingleside answered 23/12, 2010 at 15:30 Comment(3)
You are correct about not wanting to generate the expensive calls. A secondary reason is that copy-in-copy-out semantics have different semantics than reference semantics and it would be inconsistent to have two subtly different semantics for ref passing. (That said, there are some rare situations in which compiled expression trees do copy-in-copy-out, unfortunately.)Dolomite
What's really needed is a bigger variety of parameter-passing modes, so that the compiler could substitute "copy in/copy out" where appropriate, but squawk in cases where it isn't.Approval
The workaround wouldn't work with Interlocked.Add. Suddenly it wouldn't be atomic anymore.Colbycolbye
N
47

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); 
bd.Budget = k;
Neckcloth answered 23/12, 2010 at 13:2 Comment(7)
why to use one extra variable ??Impress
@pratik: the out modifier should not be initialized previously.Neckcloth
@pratik You can't pass in a property as an out parameter because there's no guarantee that the property actually has a setter, so you need the extra variable.Fogged
@mjd79: Your reasoning is incorrect. The compiler knows whether there is a setter or not. Suppose there was a setter; should it be allowed?Dolomite
Hmm - I would still think no. The out keyword defines a parameter that is passed by reference, and upon completion of the call, the value at that reference is set. Since properties in C# are (basically) wrappers around method calls, and not the actual underlying fields, you wouldn't be able to set the value of the property. Is that right?Fogged
@dhinesh, i think the OP is looking for an answer of why he can't do it, not just what he must do instead. Read the answer from Hans Passant and the comments from Eric Lippert.Vespiary
@Neckcloth The "real" reason why he can't do it is because he's using C# rather than VB which DOES allow this. I'm from the VB world (obviously?) and I'm often surprised at the extra restrictions C# imposes.Corrinnecorrival
V
14

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;
}
Voltaic answered 19/9, 2014 at 13:33 Comment(1)
Too bad is that if you have a method where you also need to READ the value, you have to provide it twice (I think). Once for reading, once for setting. Sometimes I'm pretty autistic with duplicate code. I could i.e. change the setter property, but not the getter property. If anyone knows a workaround to solve that, that would be great!Zloty
N
10

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.

Neglectful answered 23/12, 2010 at 13:3 Comment(1)
@E.vanderSpoel Fortunately I lifted the content out, removed the link.Neglectful
M
8

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;
Metamorphism answered 3/11, 2018 at 11:40 Comment(1)
I would use the return to check if the parse was successful in case of invalid input.Scutcheon
C
2

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;
Chilton answered 23/12, 2010 at 13:3 Comment(1)
Thanks.But may i know why so?Impress
F
2

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.

Foretell answered 3/9, 2022 at 22:43 Comment(0)
E
0

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));
Erebus answered 7/10, 2020 at 16:3 Comment(0)
J
0

if you using "ref" keyword please don't use "properties" it brings a lot of problems and errors in compile time. keep it old school and just use plain and simple fields. so this is the answer:

class BudgetAllocate
{
 private double budget;
 public double Budget { get => budget; set => budget = value}
}
Jitney answered 30/1 at 12:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.