118

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.

Asef Hossini
  • 655
  • 8
  • 11
Pratik
  • 11,534
  • 22
  • 69
  • 99
  • In bd.Budget bd is an object of class BudgetAllocate. Sorry I forgot. – Pratik Dec 23 '10 at 13:02
  • possible duplicate of [C# property and ref parameter, why no sugar?](http://stackoverflow.com/questions/529782/c-sharp-property-and-ref-parameter-why-no-sugar) – Peter O. Apr 20 '13 at 01:31
  • 1
    possible duplicate of [Accessing properties through Generic type parameter](http://stackoverflow.com/questions/3059420/accessing-properties-through-generic-type-parameter) – Chris Moschini Nov 03 '13 at 20:05
  • 1
    Possible duplicate of [Passing properties by reference in C#](http://stackoverflow.com/questions/1402803/passing-properties-by-reference-in-c-sharp) – Legolas Nov 07 '16 at 16:02
  • 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. – jxramos Dec 19 '17 at 20:00

9 Answers9

186

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.

Mike Chamberlain
  • 39,692
  • 27
  • 110
  • 158
  • 9
    @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. – Eric Lippert Mar 21 '19 at 13:41
  • 9
    @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**. – Eric Lippert Mar 21 '19 at 13:42
  • @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. – Eric Lippert Mar 21 '22 at 22:21
  • @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. – Eric Lippert Mar 21 '22 at 23:05
  • @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. – MichaelS Mar 21 '22 at 23:22
77

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

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 16
    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.) – Eric Lippert Dec 23 '10 at 15:45
  • 2
    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. – supercat Feb 01 '13 at 23:42
44

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;
big boy
  • 315
  • 2
  • 13
dhinesh
  • 4,676
  • 2
  • 26
  • 44
  • 14
    why to use one extra variable ?? – Pratik Dec 23 '10 at 13:05
  • @pratik: the out modifier should not be initialized previously. – dhinesh Dec 23 '10 at 13:06
  • 8
    @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. – matt Dec 23 '10 at 13:35
  • 27
    @mjd79: Your reasoning is incorrect. The compiler knows whether there is a setter or not. Suppose there was a setter; should it be allowed? – Eric Lippert Dec 23 '10 at 15:41
  • 1
    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? – matt Dec 23 '10 at 17:41
  • 21
    @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. – slugster Dec 23 '10 at 21:02
  • 2
    @dhinesh 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. – SteveCinq Dec 09 '17 at 05:11
13

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;
}
David Hollinshead
  • 1,710
  • 17
  • 18
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.

Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
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;
DanDan
  • 1,038
  • 4
  • 15
  • 28
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;
Adriaan Stander
  • 162,879
  • 31
  • 289
  • 284
1

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

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));
clamchoda
  • 4,411
  • 2
  • 36
  • 74