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();
}
}
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.
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).
Usually, if the object was a class instance, I'd use reflection to set values to its members. Consider the following:
class Foo
{
public Control _tCtrl { get; set; }
public Object _tObj { get; set; }
public void Set()
{
// How do I give tObj the converted value of _tCtrl.ToString() ?
// var val = Convert.ChangeType( _tCtrl.ToString(), tObj.GetType() );
// _tObj.SetValue( val );
}
}
Calling it would be like:
Class Bar
{
public Int32 _i { get; set; }
}
Bar bar = new Bar();
Foo foo = new Foo();
foo._tCtrl = new TextBox() { Text = "100" };
foo._tObj = bar._i;
foo.Set();
Or:
Foo foo = new Foo();
Int32 i;
foo._tCtrl = new TextBox() { Text = "100" };
foo._tObj = i;
foo.Set();
Anything could be passed into Set(). Assume that I can convert from a String to whatever type is tObj.GetType().
I think what this boils down to is converting a string to any other type. That has problems. How would you convert a string to, say, a Form, a List<>, or a Random? It would require you to write a converter for every type in the .NET Framework (or possibly every existing type in the universe).
You shouldn't be trying to do this. I don't know what you are trying to achieve, but there is almost surely a better way.
There is no way to allow that kind of syntax, but you can do the following:
foo.Set(bar, "_i");
Where Set uses the following:
type.GetProperty(propertyName, Type.EmptyTypes).SetValue(tObj, value, Type.EmptyTypes);
Where propertyName, tObj, and value are defined elsewhere.
This is pretty closely related to another SO question.
Using the example below, could someone explain to me why adding a new List<Foo> where each of Foo's properties are explicitly set causes the ApplicationSettingsBase.Save() method to correctly store the data, whereas adding a new Foo to the list via a constructor (where the constructor sets the property values) does not work? Thanks!
public class Foo
{
public Foo(string blah, string doh)
{
this.Blah = blah;
this.Doh = doh;
}
public Foo() { }
public string Blah { get; set; }
public string Doh { get; set; }
}
public sealed class MySettings : ApplicationSettingsBase
{
[UserScopedSetting]
public List<Foo> MyFoos
{
get { return (List<Foo>)this["MyFoos"]; }
set { this["MyFoos"] = value; }
}
}
// Here's the question...
private void button1_Click(object sender, EventArgs e)
{
MySettings mySettings = new MySettings();
// Adding new Foo's to the list using this block of code doesn't work.
List<Foo> theList = new List<Foo>()
{
new Foo("doesn't","work")
};
// But using this block of code DOES work.
List<Foo> theList = new List<Foo>()
{
new Foo() {Blah = "DOES", Doh = "work"}
};
// NOTE: I never ran both the above code blocks simultaneously. I commented
// one or the other out each time I ran the code so that `theList` was
// only created once.
mySettings.MyFoos = theList;
mySettings.Save();
}
This might be due to how you constructed your example. But using the given code, the "doesn't work" list is getting removed when you do the "does work" section. If you want both elements to be in theList at the end of the method, you can only have one new List<Foo>() call.
I stumbled upon the answer while trying to clarify my question just now. If I supply a default constructor to the Foo class:
public Foo() { }
--leaving everything else the same--then the values of the class are correctly stored in the user.config file when ApplicationSettingsBase.Save() is executed. Weird.