I have this data class:
class MyClass
{
Foo Foo1 { get; set; }
Foo Foo2 { get; set; }
Foo Foo3 { get; set; }
Foo Foo4 { get; set; }
}
Now, somewhere else in my program, I have this function:
void ModifyMyClass(MyClass myClassInstance)
{
var rightFooToModify = myclassInstance.Foo1;
rightFooToModify = new Foo(); // Here, my intention is to
// modify MyClass, not just the
// local variable 'rightFooToModify'
}
I need to be able to choose one of Foo1, Foo2, Foo3, Foo4 (depending on some conditions) and then set it to a new Foo object.
What would be the best way to achieve this? Are there some kind of reference variable that I can use for this in C#?
EDIT:
I could use (and it is actually what I use right now):
myClassInstance.Foo1 = new Foo();
But, ModifyMyClass() is actually a bit more complicated, for example:
void ModifyMyClass(MyClass myClassInstance)
{
if (someCondition)
{
var rightFooToModify = myClassInstance.Foo1
// ... Do some computation with rightFooToModify
myClassInstance.Foo1 = new Foo(/* some parameters */);
}
else if (someOtherCondition)
{
// The exact same code as the if above , but with
// myClassInstance.Foo2 instance.
}
}
I would like to avoid duplicating the code in the if block in all the following else if. That is why I would like to just use var rightFooToModify = myClassInstance.Foo1 at the start of the method and not duplicate the code needlessly in mutliple ifs.
You can think of it like this
When you assign your property to a local variable,
var rightFooToModify = myclassInstance.Foo1;
all you are doing is writing a post-it-Note with the location of some memory on.
If someone gives you another post-it-note with some more, replacing the original one
rightFooToModify = new Foo();
It doesn't change the original memory, its still there, nothing happened.
This is how pointers/references work. They point/reference a location in memory, you can copy a reference yet overwriting that reference doesn't do anything to the original memory location, you just have a new post-it-note pointing to somewhere else.
However, now for a really contrived example of a way to overcome this with ref return and ref Local
Disclaimer, i don't really advocate doing this in your situation, however for sheer academic purposes lets look at this language
feature
Given
public class MyClass
{
private Foo _foo1;
public Foo Foo1
{
get => _foo1;
set => _foo1 = value;
}
public ref Foo ModifyMyClass()
{
return ref _foo1;
}
}
Example
// create class
var someClass = new MyClass();
// create some memory for it
someClass.Foo1 = new Foo();
someClass.Foo1.SomeValue = "test";
// copy the reference to it
var copy = someClass.Foo1;
// overwrite it
copy = new Foo();
copy.SomeValue = "test2";
// oh nossssssssss!!!! is didn't change!!
Console.WriteLine(someClass.Foo1.SomeValue);
//enter ref local
ref var actualLocation = ref someClass.ModifyMyClass();
// create some memory
actualLocation = new Foo();
actualLocation.SomeValue = "test3";
// omg it worked!!!!
Console.WriteLine(someClass.Foo1.SomeValue);
Output
test
test3
And yet another bizarre and weird usage of ref
someClass.ModifyMyClass() = new Foo();
someClass.ModifyMyClass().SomeValue = "I hope i never see this in my code base";
Console.WriteLine(someClass.Foo1.SomeValue);
I don't know if this will be of any help, but you can have a method returning the correct setter of type Action<MyClass, Foo> , i.e.
Action<MyClass, Foo> GetFooSetter()
{
if (someCondition)
{
return (myClass, foo) => myClass.Foo1 = foo;
}
else if (someOtherCondition)
{
return (myClass, foo) => myClass.Foo2 = foo;
}
}
and then call it like this:
GetFooSetter()(myClassInstance, new Foo());
The use case is not so clear, so I will suggest two options:
If Foo is not immutable, then just modify it's values instead of assigning it to a new instance. You can even add a copy function to copy values from another Foo:
Foo fooToChange = myClassInstance.Foo1;
fooToChange.CopyFrom(new Foo());
Refactor to hold the Foos in an array, then you can ask for it and assign with an index:
class MyClass
{
public Foo[] Foos = new Foo[4];
}
int fooToChange = 3;
myClassInstance.Foos[3] = new Foo();
Related
I have two struct instances that have a reference-type variable. I want to swap those two variables, but my first guess on how to do this looks like it doesn't work. My code looks something like this:
struct Foo
{
Bar m_bar;
}
void Main()
{
Foo a = new Foo();
Foo b = new Foo();
//swapping the values of m_bar of a and b
Bar temp = a.m_bar;
a.m_bar = b.m_bar;
b.m_bar = temp;
}
From what I could gather, it looks like since the variables are reference types, assigning b.m_bar to a.m_bar also assigns it to temp, messing up the swap.
So is my code wrong? If yes, what's the right way to swap two reference-type variables? If not, I guess my code messes up somewhere else.
Thanks!
The code in the question works fine, as shown in the minimal repro below. The confusion here is almost certainly something to do with mutable value-types; structs with mutable fields are notorious for confusion. I strongly recommend treating Foo as immutable, explicitly making it a readonly struct if your compiler supports it - something like:
readonly struct Foo
{
public Bar Bar { get; }
public Foo(Bar bar) => Bar = bar;
// not shown: override ToString, GetHashCode and Equals
}
But: with something more like your original code:
static class P
{
static void Main()
{
Foo a = new Foo { m_bar = new Bar("abc") };
Foo b = new Foo { m_bar = new Bar("def") };
System.Console.WriteLine("Before");
System.Console.WriteLine($"a.m_bar: {a.m_bar}"); // abc
System.Console.WriteLine($"b.m_bar: {b.m_bar}"); // def
//swapping the values of m_bar of a and b
Bar temp = a.m_bar;
a.m_bar = b.m_bar;
b.m_bar = temp;
System.Console.WriteLine("After");
System.Console.WriteLine($"a.m_bar: {a.m_bar}"); // def
System.Console.WriteLine($"b.m_bar: {b.m_bar}"); // abc
}
struct Foo
{ // note: public fields are usually a bad idea in any type
// note: mutable fields on value-types are usually a bad idea
public Bar m_bar;
}
class Bar
{
public string Name { get; }
public override string ToString() => Name;
public Bar(string name) => Name = name;
}
}
In the following test code I do not understand why the first line of TestMethod is legal, but the remaining two lines are not:
public class Bar
{
public string Prop { get; set; }
}
public class Foo
{
public int Primitive { get; } = 0;
public Func<int, int> Function { get; } = (i) => i;
public Bar Bar { get; } = new Bar();
}
public class TestClass
{
public void TestMethod()
{
var baz = new Foo { Bar = { Prop = "Hello World!" } }; // legal
var buzz = new Foo { Primitive = 1 }; // Property or indexer 'Foo.Primitive' cannot be assigned to -- it is read only
var fuzz = new Foo { Function = (i) => 2 }; // Property or indexer 'Foo.Function' cannot be assigned to -- it is read only
}
}
If it is legal to assign to class-type read only properties like Bar in an object initializer (which it is; and which makes sense, since 'read only' really means 'read only except at class construction time' in C# as I understand it) then why is it illegal to assign to properties with types like like int and Func<int, int>?
This seems even more confusing since (again, as I understand it) Func<int, int> is a reference type, like the Bar property but unlike the int property.
var baz = new Foo { Bar = { Prop = "Hello World!" } }; // legal
This is not an assignment to Bar. It is essentially:
var tmp = new Foo();
tmp.Bar.Prop = "Hello World!";
var baz = tmp;
At no point is .Bar assigned to.
Conversely, however:
var buzz = new Foo { Primitive = 1 };
is:
var tmp = new Foo();
tmp.Primitive = 1;
var buzz = tmp;
which does assign to .Primitive.
If it is legal to assign to class-type read only properties like Bar in an object initializer (which it is [...])
No, it isn't. Object initializers call the constructor and then assign to properties. For example, this code:
var buzz = new Foo { Primitive = 1 };
is just syntactic sugar for this:
var buzz = new Foo();
buzz.Primitive = 1;
That's not valid if Primitive is a read-only property.
(To be very pedantic, it's more generally appropriate to regard it as assigning to a temporary local variable, setting the properties, and then assigning to buzz at the very end, but we'll ignore that for now.)
The code that you've observed working isn't setting those read-only properties - it's getting them, and then setting values using the returned reference. So this:
var baz = new Foo { Bar = { Prop = "Hello World!" } }
is actually equivalent to:
var baz = new Foo();
baz.Bar.Prop "Hello World!";
That's entirely valid, even though Bar is read-only.
I have the following object
var filters = new List<IReportFilter>
{
new ReportFilter
{
ReportColumn = new ReportColumn{ ColumnKey = "Result.IsCompleted"},
Value = "1",
SubFilters = new List<IReportFilter>
{
new ReportFilter { SqlOperator = FilterOperator.Or, ReportColumn = new ReportColumn{ ColumnKey = "User.LastName"}, Value = "Alhayek"},
new ReportFilter { SqlOperator = FilterOperator.Or, ReportColumn = new ReportColumn{ ColumnKey = "User.LastName"}, Value = "Smith"},
new ReportFilter { SqlOperator = FilterOperator.Or, ReportColumn = new ReportColumn{ AggregateFunction = SqlAggregateFunctions.Count}, Type = FilterType.GreaterThenOrEqualTo ,Value = "0" },
}
},
};
The obove object is passed to another class using a method like so
IReportModel ReportModel = Template.CreateReport();
ReportModel.Get(filters);
Inside the the Get method of the ReportModel class I want to loop through the filters list and create a new list without changing the original list. the new list will become a subset of the original.
From with in my Get method here is what I have done
public SqlCommand Build(List<IReportFilter> filters)
{
var a = CloneFilters(filters);
var b = CloneFilters(filters);
List<IReportFilter> standardFilters = ExtractFiltersByAType(a, true);
List<IReportFilter> aggregateFilter = ExtractFiltersByAType(b, false);
}
But every time I execute the method ExtractFiltersByAType the value of a,b, and filters change to equal the same value of aggregateFilter.
I am NOT expecting for any of the variables to change. But they are for some reason that I don't understand.
Here is my CloneFilters method
private List<IReportFilter> CloneFilters(List<IReportFilter> myFilters)
{
List<IReportFilter> copyOfFilters = new List<IReportFilter>();
foreach (var myFilter in myFilters)
{
copyOfFilters.Add(myFilter);
}
return copyOfFilters;
}
And here is my ExtractFiltersByAType
private List<IReportFilter> ExtractFiltersByAType(List<IReportFilter> filtersSource, bool IsStandard = true)
{
List<IReportFilter> validFilters = new List<IReportFilter>();
foreach (var filterSource in filtersSource)
{
if (filterSource.SubFilters != null && filterSource.SubFilters.Any())
{
filterSource.SubFilters = ExtractFiltersByAType(filterSource.SubFilters, IsStandard); //I think this what could be causing this problem
}
if ((IsStandard && !filterSource.ReportColumn.IsAggregate) || (!IsStandard && filterSource.ReportColumn.IsAggregate))
{
validFilters.Add(filterSource);
}
}
return validFilters;
}
Question
Since I am not using ref to pass the object by reference to the method, why is my function changing the value to original object?
When passing a list of object to method in c#, will the system create a copy or will it passes the object by reference?
How can I solve this problem so that every time I execute ExtractFiltersByAType method, only the copy is changed not the originals?
I am thinking that the line filterSource.SubFilters = ExtractFiltersByAType(filterSource.SubFilters, IsStandard); in the ExtractFiltersByAType is causing the problem but I don't understand why and how.
Without ref
When you pass a reference type as an argument (which includes a list), you pass a copy of the reference to that object. This means you can change your object attributes, but can't change the object itself.
Example:
public class Program
{
static void Main(string[] args)
{
Foo foo = new Foo(1);
Console.WriteLine(foo.Bar);
// This will change foo.Bar
ChangeFoo(foo, 5);
Console.WriteLine(foo.Bar);
// Does not change foo
DoesNotChangeFoo(foo, 10);
Console.WriteLine(foo.Bar);
Console.Read();
}
static void ChangeFoo(Foo foo, int newValue)
{
// Since it receives a copy of the reference to Foo, it actually changes foo.Bar value
foo.Bar = newValue;
}
static void DoesNotChangeFoo(Foo foo, int newValue)
{
// Since it receives a copy of the reference to foo, it only updates this method's reference, not changing the caller's reference
foo = new Foo(newValue);
}
}
public class Foo
{
public Foo(int bar)
{
Bar = bar;
}
public int Bar { get; set; }
}
With ref
If you wanted to change the caller's object reference, you would need to pass the actual reference used by the calle's, that's when you use the ref keyword.
Example:
public class Program
{
static void Main(string[] args)
{
Foo foo = new Foo(1);
Console.WriteLine(foo.Bar);
// This will change foo's object reference
ChangeFooObjectReference(ref foo, 15);
Console.WriteLine(foo.Bar);
Console.Read();
}
static void ChangeFooObjectReference(ref Foo foo, int newValue)
{
// SInce you are receiving the actual reference used by the caller, you actually change it's own reference
foo = new Foo(newValue);
}
}
public class Foo
{
public Foo(int bar)
{
Bar = bar;
}
public int Bar { get; set; }
}
Your case
As you correcly assumed, the main cause of your problem is this line:
filterSource.SubFilters = ExtractFiltersByAType(filterSource.SubFilters, IsStandard);
This line actually changes this object's SubFilters.
But it's worth noting that you may have some bigger problems in you Clone method.
private List<IReportFilter> CloneFilters(List<IReportFilter> myFilters)
{
List<IReportFilter> copyOfFilters = new List<IReportFilter>();
foreach (var myFilter in myFilters)
{
copyOfFilters.Add(myFilter);
}
return copyOfFilters;
}
This method return's a new List, but the content of that list is exactly the same as the argument's. This means that, if you change any of the object's contained in the object used as an argument, you change it in the new List too.
Here's an example of what's happening.
static void Main(string[] args)
{
List<Foo> foos = new List<Foo>();
foos.Add(new Foo(2));
List<Foo> newFoo = CreateNewFoo(foos);
Console.WriteLine(newFoo.First().Bar);
foos.First().Bar = 5;
// Since we changed the first object of the old list, and it is the same object in the new list, we will get the new result.
Console.WriteLine(newFoo.First().Bar);
Console.Read();
}
static List<Foo> CreateNewFoo(List<Foo> foos)
{
List<Foo> newFoos = new List<Foo>();
foreach(Foo foo in foos)
{
newFoos.Add(foo);
}
return newFoos;
}
I would suggest implementing the ICloneable interface in your IReportFilter interface, and each concrete class implementing IReportFilter.
ICloneable implements a single method Clone(), which returns an object. This method should create a new instance of the same class in which it's implemented, containing a new object identical to the current object. Than you would change your method to:
private List<IReportFilter> CloneFilters(List<IReportFilter> myFilters)
{
List<IReportFilter> copyOfFilters = new List<IReportFilter>();
foreach (var myFilter in myFilters)
{
copyOfFilters.Add(myFilter.Clone() as IReportFilter);
}
return copyOfFilters;
}
As for implementing the ICloneable inteface, refer to this question:
Proper way to implement ICloneable
Edit
As mentioned by user muratgu in the question comments, your CloneFilter method is doing a shallow copy of your list, what you are looking for is a deep copy. That could be implemented with the aforementioned ICloneable interface.
The only thing that ref does is determine whether the method receiving the parameter can modify the variable passed to it. In the case of an object, that means setting the variable to a different object or setting it to null.
But even if you don't use ref, the method you pass the parameter to can can set its properties or call methods which modify the state of that object. That's normal and expected. When you pass an object to another method, it's not just so that the other method can read its properties. You might also want that method to operate on that object in some way that modifies it.
The simplest example is a List<T>. If you pass a List<string> to another method - without using ref - that other method can add items to the list, modify items in the list, clear the list, etc.
The only difference with using ref is that if the method you pass the variable to sets the variable to a different list or sets it to null, it's going to modify the variable in the method that passed the argument.
Most of the time we don't want a method we call to completely replace the variable we pass in. If we wanted a new object we'd write a function that returns an object, not one that replaces the variable we passed in.
I have a simple class which has boolean field:
public struct Foo { bool isAvailable; }
Now I have a List of foos:
List < Foo > list = new List< Foo >();
Later on I enumerate each foo in the list and try to update its isAvailable field:
foreach(Foo foo in list) {
foo.isAvailable = true;
}
But the above code never updates the list. What am I doing wrong here and what's its remedy.
It's because Foo is a mutable struct.
When you fetch the value from the list, it's making a copy - because that's how value types behave. You're changing the copy, leaving the original value unchanged.
Suggestions:
You probably should be using a class
Don't create mutable structs. They behave in ways which can be hard to predict, or at least not the way you might expect when you're not explicitly thinking about it.
While you could change your code to iterate over the list in a different way and replace the value each time, it's generally a bad idea to do so. Just use a class... or project your list to a new list with the appropriate values.
Original answer, when Foo was a class
It should work fine. For example, here's a short but complete program which does work:
using System.Collections.Generic;
public class Foo
{
public bool IsAvailable { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name + ": " + IsAvailable;
}
}
class Test
{
static void Main()
{
List<Foo> list = new List<Foo>()
{
new Foo { Name = "First", IsAvailable = true },
new Foo { Name = "Second", IsAvailable = false },
new Foo { Name = "Third", IsAvailable = false },
};
Console.WriteLine("Before:");
list.ForEach(Console.WriteLine);
Console.WriteLine();
foreach (Foo foo in list)
{
foo.IsAvailable = true;
}
Console.WriteLine("After:");
list.ForEach(Console.WriteLine);
}
}
Try to adapt your current code to a similar short but complete program which doesn't work, post that, and we can work out what's going on.
You're using a struct as Foo, not a class. Structs are copied, not referenced, and therefore you only modify the copy and not the object stored in the list.
So you basically have two options:
Make it a class
Re-assign the result to the list. To do so, I'd iterate using an index instead of using foreach.
When you fill list, you need to create new istance for each Foo Class.
List list = new List();
Foo foo = new Foo();
foo.isAvailable = false;
list.Add(foo);
foo = new Foo();
list.Add(foo);
foo = new Foo();
list.Add(foo);
if you fill on this way:
List list = new List();
Foo foo = new Foo();
list.Add(foo);
list.Add(foo);
list.Add(foo);
you are reference on same memory location on stack for each object.
This code compiles successfully, but I think it should fail to compile. Also, when you run it you get a NullReferenceException. The missing code is the "new Bar" in the initialization of the Bar property.
class Bar
{
public string Name { get; set; }
}
class Foo
{
public Bar Bar { get; set; }
}
class Program
{
static void Main(string[] args)
{
var foo = new Foo
{
Bar = { Name = "Hello" }
};
}
}
Is this a known bug?
Why do you think it should fail to compile? It is nested object initializer syntax, and it is the responsibility of the client code to provide a valid value for initialization.
From the documentation:
C# spec 7.5.10.2 "Object initializers"
A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property
No this is not a bug.
If you want it to run you either put a new before Bar (just like you did for Foo before the initializer) or you create the Bar object in Foo's constructor.
The object initializer is essentially just syntactic sugar.
This:
var foo = new Foo
{
Bar = { Name = "Hello" }
};
Is exactly the same as this:
var foo = new Foo();
foo.Bar.Name = "Hello";
The new is unecessary in an object initializer:
object-creation-expression:
new type ( argument-list(opt) ) object-or-collection-initializer(opt)
new type object-or-collection-initializer
object-or-collection-initializer:
object-initializer
collection-initializer
object-initializer:
{ member-initializer-list(opt) }
{ member-initializer-list , }
initializer-value:
expression
object-or-collection-initializer
It's that last one that is most important. It represents the right-hand-side of your property = value syntax. This means that the right-hand-side can be a normal c# expression (with a new operator) or another initalizer. In which case, all you need are the opening and closing braces.
If you change your code to the following equivalent, you will also get a runtime error of a NullReferenceException instead of a compile time error/warning.
static void Main(string[] args) {
Foo foo2 = new Foo();
foo2.Bar.Name = "test";
}
The effect is the same, Bar is never properly initialized. Now, from a compiler writers perspective it is extremely difficult to determine in all cases as to whether Bar was properly initialized prior to use.
...
Bar = { Name = "Hello"}
...
means: Foo.Bar.Name="Hello"
not: {Foo.Bar=new Bar(); Foo.Bar.Name="Hello";}
This will compile and will not throw any exception, so it's not a bug, you're just initializing an unexisting object:
class Bar
{
public string Name;
}
class Foo
{
private Bar _bar = new Bar();
public Bar Bar
{
get { return _bar; }
set { _bar = value; }
}
}
class Program
{
static void Main(string[] args)
{
Foo foo = new Foo
{
Bar = { Name = "Hello"}
};
}
}
Bar is a property of Foo, so it is allowing you to access it and assigning a name property at compile time, but at run time it checks for the valid instance of Bar which is not present so throwing null reference exception, it will be the case with any C# version.
I create a working sample.
Its easy, only add a "new Bar()" an it work fine
class Bar
{
public string Name { get; set; }
}
class Foo
{
public Bar Bar { get; set; }
}
class Program
{
static void Main(string[] args)
{
var foo = new Foo
{
Bar = new Bar() { Name = "Hello" }
};
Console.WriteLine(foo.Bar.Name);
Console.ReadLine();
}
}