Consider the following class:
class Foo
{
public string Bar { get; set; } = "foobar";
}
And this piece of code:
var foo = new Foo {
Bar = bar == null
? null
: bar
};
Obviously, the value of Bar would be null after the execution of this code (suppose that bar = null).
I want the constructor initializer to use default property value in given cases (e.g. when bar is null). I want to know if there is an easier way to do this instead of using:
if (bar == null) {
foo = new Foo();
} else {
foo = new Foo { Bar = bar };
}
Or
foo = new Foo();
if (bar != null)
foo.Bar = bar;
Well, you can simplify it by using null coalescing operator:
var foo = new Foo();
foo.Bar = bar ?? foo.Bar;
Or you can change property to check for null values and ignore them :
private string _bar = "foobar";
public string Bar
{
get { return _bar; }
set { _bar = value ?? _bar; }
}
Then you can use this code to instantiate Foo :
var foo = new Foo() { Bar = bar };
Note that now if bar is null its value will be ignored in property's setter.
Easiest and most readable (IMHO) solution would be:
var foo = new Foo();
if (bar != null)
foo.Bar = bar;
There is no way to make the validation like you suggested in the initializer (at least not as of C# 6). You could use some constructs with extracting your default to constant like other answers here suggest, but this takes away from readability and does not make using the class easier - you have to know about implementation (default value in the constant) details, which breaks the encapsulation.
If your main concern is about code style, I suggest you get used to ifs, because there is nothing wrong with them and are easy to understand for someone else maintaining your code or yourself in a few months.
If there is something else you need, like validation of the property value, you need to place it in the setter itself (and you should have stated so in the question).
The cleanest OO way of doing that would be by using overloaded constructor and a factory method:
class Foo
{
public Foo Create(string bar)
{
return bar == null ? new Foo() : new Foo(bar);
}
public Foo() : this("foobar")
{
}
public Foo(string bar)
{
Bar = bar;
}
public string Bar { get; }
}
If you'll extract the default value of the Bar property to a static field:
public class Foo
{
public string Bar { get; set; } = defaultBarValue;
public static string defaultBarValue = "foobar";
}
You'll be able to do this:
new Foo
{
Bar = bar != null ? bar : Foo.defaultBarValue,
};
But I doubt it's worth the effort...
You could do:
class Foo
{
public const string BarDefault = "foobar";
public string Bar { get; set; } = BarDefault;
}
var foo = new Foo { Bar = bar ?? Foo.BarDefault };
It has an advantage of having all your defaults in consts at the top of the class but I don't really see the point personally. It does however mean you don't need a conditional statement (kind of).
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 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();
I looked through similar threads and they all pertain to C++, so I assume it's better to ask than to just fumble around. I have, for example, this code:
foo[] fooArray = new foo[5];
fooArray[2] = new bar();
Say that foo is a custom class with no variables/methods:
public class foo
{
}
and that bar is a custom class derived from foo:
public class bar : foo
{
int fooBar = 0;
}
In my fooArray, I need to access the variable fooBar from fooArray[2], which is a bar, but as the variable doesn't appear in the base class, it doesn't show up in an array of foos. Is there any way I can access it?
Edit: In the application code, both foo and bar have the required constructors and other settings.
You could cast. In order to be safe you should use the as keyword:
bar b = fooArray[2] as bar
if ( b != null ){
//Do stuff with b.foobar
} else {
//Handle the case where it wasn't actually a bar
}
You can cast one of the items in the array to bar and as access it that way,
bar barVar = (bar)fooArray[2];
int fooBarInt = barVar.fooBar;
Or use the as operator to treat the object as type bar,
bar barVar = fooArray[2] as bar;
if (barVar != null)
{
// user barVar.fooBar;
}
However, the way it is define in your example fooBar is private. You would have to make it public to access it outside of the class.
public class bar : foo
{
public int fooBar = 0;
}
Because foo class don't have fooBar field.You can't access it unless you cast your variable to Bar:
fooArray[2] = new bar();
var value = ((bar)fooArray[2]).fooBar;
Note: fooBar field should be public.Fields are private by default.
In order to access it you would need to cast the value to a bar instance
int i = ((bar)fooArray[2]).foobar;
Note that if fooArray[2] isn't actually an instance of bar this code will throw an exception. If you want to do this only if fooArray[2] is a bar then do the following
bar b = fooArray[2] as bar;
if (b != null) {
int i = b.foobar;
...
}
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();
}
}