This question already has answers here:
C# Pass a property by reference
(8 answers)
Closed 8 years ago.
I have a process that I want to apply to multiple value-type properties of an arbitrary object, such that each property is modified in some way by the process. A method that applies the process to any given property passed to it would seem to be the way to go, but because the property is a value type it doesn't get changed unless I pass it by reference, but of course the C# compiler prevents properties being passed by reference.
How can I achieve the following without the compiler objecting or having to write messy multiple lines that just repeat the same conditional code for each property?
static internal void AssignStringValueOrLeaveIfNull(string newValue, string sampleValue)
{
if (!string.IsNullOrEmpty(newValue))
sampleValue = newValue;
}
...
AssignStringValueOrLeaveIfNull(value1, anObject.SampleText1);
AssignStringValueOrLeaveIfNull(value2, anObject.SampleText2);
AssignStringValueOrLeaveIfNull(value3, anObject.SampleText3);
AssignStringValueOrLeaveIfNull(value4, anObject.SampleText4);
AssignStringValueOrLeaveIfNull(value5, anObject.SampleText5);
...etc, 30 times.
where anObject.SampleTextn are all strings.
I can't be the first person to have wanted to do something similar!
I'm using VS2008 (C#3.5)
TIA
You cannot. That concept does not exist. You would have to assign the value to a temporary local variable, use ref on the variable, and then assign it back to the property:
var tmp = anObject.SampleText1;
AssignStringValueOrLeaveIfNull(value1, ref tmp);
anObject.SampleText1 = tmp;
Or use a return value, which is probably simpler...
anObject.SampleText1 = AssignStringValueOrLeaveIfNull(value1, anObject.SampleText1);
ref works with:
fields
local variables
array elements
parameters
It does not work with properties, since properties are actually method calls, and the result of a method call does not have a sensible location to ref it from. Note: at the IL level, you can have ref return values from methods, which would theoretically allow for something akin to this - but it is not exposed in C# at the moment (if ever), and it would not work with properties as they exist today.
You could write an ugly extension method that takes an expression representative of the property you want to set, and give it a chance to check whether your new values are null or empty (or different from the destination) before assigning the value.
public static void SetPropertyValue<T>(this T target, Expression<Func<T, string>> memberLamda, string value)
{
// Check if "new value" is null or empty and bail if so
if (string.IsNullOrEmpty(value))
return;
var memberSelectorExpression = memberLamda.Body as MemberExpression;
if (memberSelectorExpression != null)
{
var property = memberSelectorExpression.Member as PropertyInfo;
if (property != null)
{
// Get the existing value and compare against the new value
// Only set the property if it's different from the existing value
if ((string)property.GetValue(target, null) != value)
{
property.SetValue(target, value, null);
}
}
}
}
Source
And then you could use it like:
anObject.SetPropertyValue(a => a.SampleText1, value1);
anObject.SetPropertyValue(a => a.SampleText2, value2);
This should allow you to avoid having the object marked as "dirty", but is rather expensive (as Marc mentioned in a comment on his answer).
Related
for example,
if(method1().method2().method3().method4().method5().object != null)
{
var value = method1().method2().method3().method4().method5().object;
}
I have came across and written many such scenarios in c#, (ex:- while working with selenium frameworks) where a specific object can only be access through a long chain of method callings, and having to check for null before accessing objects value to not get exceptions. Feels like above code is bit too redundant. What coding pattern is/are used to make above code better? How to not write method calling chain 2 times ?
Even before C# 7, you could just use a local variable:
var something = method1().method2().method3().method4().method5().object;
if (something != null)
{
// Use something
}
In C# 7+ you can also use a pattern:
if (method1().method2().method3().method4().method5().object is object value)
{
// Use value
}
Note that you can't use var for this; the var pattern matches against a null, whereas you want to only match if it's non-null. Just use the appropriate type. (We can't tell what it is from the question...)
You can use the null conditional operator with a local variable. That would save the 2nd call and also checking the null at each method return. You can read about null conditional operator at here.
var result = method1()?.method2()?.method3()?.method4()?.method5()?.object;
if(result != null)
{
var value = result ;
}
I would like to make use of out or ref parameters that may not be assigned prior to calling the function. The function then is responsible for checking whether the parameter exists, and if not, creates and returns a new object.
Here is some example C# code for what I'm trying to accomplish:
public virtual object MyFunction(out object myObject)
{
if (myObject == null)
myObject = new Object();
// do some more things here...
// maybe return myObject, or perhaps something else
return myObject;
}
I would like each of the four example function calls below to be considered valid:
Object x = new Object();
MyFunction(x); // x gets passed by reference
Object y = MyFunction(x);
Object z = MyFunction();
Object u = MyFunction(null);
I get an error on line 3 telling me that Out parameter 'list' might not be initialized before accessing. Attempting to give myObject a default value gives the error A 'ref' or 'out' parameter cannot have a default value.
Is there a way to use out parameters (pass by reference) and check to see if those references have been initialized before assigning them to anything?
No, this is not possible. If you use an out parameter then you are not permitted to read the value before assigning to the variable, for the reason the compiler has already told you. If you use ref then the parameter must be initialized before it is allowed to be passed in.
Return values are most of the time the right choice when the method doesn't have anything else to return.
It only stops the caller from having to declare the variable separately. Example:
int x1;
GetValue(out x1);
Declaring a variable:
int? x1 = GetValue();
You can read more about it in thi MSDN documentation.
When you pass a null as your out parameter, e.g. by using out _, the CLR will allocate temporary storage when you first use the parameter so there is no need for the initial check that won't compile.
Step over this line (there is no exception):
The call was double result = TestMethods.DoubleValue(2.5, out _);
As a newbie I've read about the dangers of passing parameters with the ref keyword. I guess there's great potential to mess up code when a ref variable is modified in part of a program which then changes what happens elsewhere. Objects end up being very tightly coupled. (I recognize there may be places where ref is worthwhile.) What I don't yet know, and am asking about, are the alternatives.
In one program, for example, I create a generic list at startup, which I manipulate in the program's methods. In one method:
//a user is asked a question
//if the response is yes, the list is modified one way and the method returns true
//if the response is no, the list is modified a different way and the method returns false.
So the method returns a Boolean and I pass the list in as a ref. I have several similar methods, each asking users unique questions and then modifying the list in some way.
It seems like a typical alternative might be to bundle the list and a Boolean field into its own class. Somehow this seems like creating an object for nothing more than convenience, just to hold two pieces of data, with no connection to any real world entity.
So, how would you (pseudo)code a method that returns both a generic list and a Boolean value?
EDIT: Here's some actual code
private static bool AskExptQuestion(ref List<StatTest> testList)
{
Console.Write(Constants.ExptQText); //experimental groups?
string response = Console.ReadLine();
//if response==y, it's experimental
if (response == "y")
{
//so select all experimental
var q1List =
from test in testList
where test.isExperimental == true
select test;
//to copy resulting IEnumerable<List> (q1list) to generic List, must copy/cast IEnumerable to a List<t>
testList = q1List.ToList();
return true;
}
//and if response==n, it's not experimental
else
{
//so select all non-experimental
var q1List =
from test in testList
where test.isExperimental == false
select test;
testList = q1List.ToList();
return false;
}
}
Returning a list (or almost anything else, for that matter) along with its characteristic, such as a Boolean value, is a "poster child" of the ref/out feature. This pattern is used in several places in the standard .NET library:
Dictionary.TryGetValue uses this pattern, returning true or false depending on presence of the key in the dictionary, and setting a out to the return object
Integer.TryParse (and other numeric types) use this pattern, returning true when the parse is successful, and setting the value in a out parameter
The difference between ref and out is that ref gives your method an option to keep the old object/value or to supply a new one, while the out forces you to supply a new one before the method returns.
There is no point to create a new class simply to bundle two unrelated types together.
In addition, it is important to understand that modifications to method parameters can happen even in situations when a parameter is not passed by ref or out. When you pass an object of reference (i.e. class) type that is mutable, any modifications to the object done inside the method are visible in the caller.
The only difference when passing by ref or out is that you are allowed to replace the object itself with a new one, while passing a reference type without ref is restricted to mutating the incoming object itself.
From reading your example and comments, it sounds like you might just want the ability to apply some filters to a collection. Why not have your functions return the filters?
private static Predicate<StatTest> AskExptQuestion()
{
Console.Write(Constants.ExptQText); // experimental groups?
bool response = Console.ReadLine() == "y"; // maybe wrap this up in a function to read a yes/no answer
return t => t.isExperimental == response;
}
You might still need that bool return value, though, which can become an out parameter. It’s not clear what that’s for.
in c# paramaters are passed by value.
object types (classes) are implimented by a pointer to an instance in memory.
where passing an object to a method, the pointer is copied to the argument and he then points to the same instance.
if i have:
void foo(MyClass param)
{
param.x = 7;
}
and somewhere else i do:
MyClass obj = new MyClass();
obj.x = 5;
foo(obj);
then after the call to foo() the x property of obj is 7.
if foo is:
void foo(MyClass param)
{
param = new MyClass();
param = 7;
}
then my original obj will still have x equals to 5. this is because i ran over the instance that param was looking at.
now the "ref" keyword:
void foo(ref MyClass param)
{
param = new MyClass();
param = 7;
}
if i call like this:
MyClass obj = new MyClass();
obj.x = 5;
foo(ref obj);
in this combination my obj will be set to the new instance of MyClass and will have x equals 7. the ref means that the variable in the method is the same as the variable passed, and not just pointing to the same instance to start with.
I'm getting this:
private object setReportValues(object report, FormCollection values)
{
PropertyInfo[] properties = report.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
string val = values.GetValue(property.Name).ToString();
property.SetValue(report, val, null);
}
return report;
}
Exception is on string val = values.GetValue(property.Name).ToString();. Do I have to check for nulls before?
Do I have to check for nulls before?
On this line, yes:
string val = values.GetValue(property.Name).ToString()
Simply because the value of that particular property could be null.
I'm gonna go out on a limb and suggest that there's no property with the provided property.Name in values. So your call to values.GetValue returns a null. When you try to do ToString() on that null value, it complains.
In short, what does your values variable contain?
Update:
With the provided information that values is a FormsCollection it is quite probable that your properties collection contains a few properties for which you have no FormsCollection field. And what happens is that you try to get this field, it returns a null value and you call ToString on that, causing everything to break.
I would invert my strategy and loop through my FormsCollection getting the properties 1 by 1 as you encounter them. The alternative is to keep it as you have it and check for null before doing a ToString.
PS: I hope all of your properties represented on the form are strings, or things will break.
Just ran into this same issue, but I found a solution without having to use a loop:
private object setReportValues(object report, FormCollection values)
{
PropertyInfo[] properties = report.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
string val = values.GetValue(property.Name)?.ToString();
property.SetValue(report, val, null);
}
return report;
}
I fixed it by adding a ? (new feature in C# 6.0 I believe) after the ...property.Name).
Why would you force .ToString() ? null is a perfectly legal value for most things. It isn't clear what values is, so I assume that is coming from your own code, but:
object val = values.GetValue(property.Name);
property.SetValue(report, val, null);
Depending on what values is, you might also want to check the difference between "has a value, that is null" vs "doesn't have any defined value". Personally, I would expect to do something like:
object val;
if(values.TryGetValue(property.Name, out val)) {
property.SetValue(report, val, null);
}
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.
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.