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;
...
}
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).
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();
}
}
I'm trying to go the opposite way of what you would normally do.
I have two POCO classes A and B where B inherrits from A
public class A
{
public int Foo { get; set; }
}
public class B : A
{
public int Bar { get; set; }
}
B is ment as an extention to A with additional information.
I start by having an instance of class A
A a = new A { Foo = 1 };
And then I wish to extend the information in class A with the additional information and get the final class B. I could map every property from class A to the property in class B, but it does not make much sence to me:
A a = new A { Foo = 1 };
B b = new B { Foo = a.Foo, Bar = 2 };
Or in constructor
A a = new A { Foo = 1 };
B b = new B(a) { Bar = 2 }; // Mapping of value Foo is done in constructor of object B
The result is in eather case a manual mapping of values from object A to B.
There must be a smarter way to do this... any suggestions?
If you are actually changing type (rather than casting) - then if you have only a few classes, then just write conversion code - perhaps a ctor for B that accepts a template A. If you have a lot of classes... there are tricks you can do with either dynamic code or serialization. PropertyCopy in MiscUtil will do this, for example (using a dynamic Expression to do the work very quickly):
A a = new A { Foo = 1 };
B b = PropertyCopy<B>.CopyFrom(a);
b.Bar = 2;
I would regard the "smart" way as being your last suggestion - write a copy constructor for B that knows how to instantiate itself from an A.