void Foo()
{
string ID = "test";
var testctrl = new Control() {ID = (ID**!=null?ID**:ID)};
}
Is it possible to get the value of ID** in the code above? The problem is they both have the same property names.
Please ignore the fact the specific ID assignment is pointless, im just using it as an example.
Putting aside that the code can't possibly compile...
The first ID (ID=) in this syntax refers to the assignment, specifically the member being assigned. On the right-hand-side, it comes down to standard resolution. Which never means the ID of the new object. It will first look at the local ID variable, then after that members (fields etc) on the current instance. To prefer members on the current instance, use this.ID.
You might also want to consider the null-coalescing operator, so if you mean "the local-variable, or the instance-member if it is null, that would be ID ?? this.ID - but an even better idea would be to make unambiguous variable names ;-p
First remark about this line, it looks strange (assigning string to bool):
bool ID = "test";
Second remark, this works fine:
string ID = "test";
var testctrl = new Control(){ ID = (ID==null?ID:ID) };
Third remark: it is a convention in C# to name your local variables start with lowercase letter.
If you want to check if the new controls id is null then you should do this.
void Foo()
{
string ID = "test";
var testctrl = new Control();
if(testctrl.ID==null)
testctrl.ID = ID;
}
The question is not that clear to me, but do you want this:
void Foo()
{
bool ID = "test";
var testctrl = new Control(){ID = (this.ID==null?ID:this.ID)};
}
note, untested.
Seeing your example, I suspect that you don't have the requirement that the ID should be unique?
In general, to avoid nasty problems like this, avoid try using duplicate property names, but maybe your bound to an existing interface.
How about adding an constructor overload which takes the 'outer' ID as a parameter?
void Foo()
{
string ID = "test";
var testctrl = new Control(ID);
}
I think in general, if the property does more than just assignment (argument checking in this case), you should do it in the 'set' of the property itself and not during 'object initialization'.
Related
In C# 8.0, I can modify the value inside a tuple directly by accessing the field name:
(string name, int score) student = ("Tom", 100);
student.name = "Jack";
Console.WriteLine(student);
And I can modify the list element's property as follow:
var list = new List<Student>(); // assume I have a Student class which has a Name property
list.Add(new Student { Name = "Tom" });
list[0].Name = "Jack";
Console.WriteLine(list[0]);
But why can't I modify the tuple-type element's value like this?
var list = new List<(string name, int score)>();
list.Add(("Tom", 100));
list[0].name = "Jack"; // Error!
Console.WriteLine(list[0]);
A tuple (ValueTuple) is a struct. Rather than returning a reference to the value as is the case with your Student example, you would actually recieve a copy of the tuple.
Changes to that copy wouldn't be reflected in the list and would be discarded. The compiler is smart enough to recognize this and stops you from doing it.
If it did compile, it would be to something similar to the following:
var list = new List<(string name, int score)>(); list.Add(("Tom", 100));
var copy = list[0];
copy.name = "Jack";
Console.WriteLine(copy.name); // Jack
Console.WriteLine(list[0].name); // still Tom
Mutable structs can be dangerous if you don't use them properly. The compiler is simply doing its job.
You can work around this with the following:
var list = new List<(string name, int score)>(); list.Add(("Tom", 100));
var copy = list[0];
copy.name = "Jack";
list[0] = copy; // put it back
Console.WriteLine(copy.name); // Jack
Console.WriteLine(list[0].name); // Jack
Try It Online
If you use an array (string, int)[] instead of a List<(string, int)>, this isn't a problem due to the way array element access works:
var arr = new (string name, int score) [] { ( "Tom", 10 ) };
arr[0].name = "Jack";
Console.WriteLine(arr[0].name); // Jack
Try It Online
This behavior is not unique to List or your tuple type. You'll experience this issue with any collection where the element is a Value Type (unless of course they offer a ref element accessor).
Note that there are similar issues when having a readonly field of a mutable value type that mutates via method calls. This can be much more insidious as no error or warning is emitted:
struct MutableStruct {
public int Val;
public void Mutate(int newVal) {
Val = newVal;
}
}
class Test {
private readonly MutableStruct msReadonly;
private MutableStruct msNormal;
public Test() {
msNormal = msReadonly = new MutableStruct(){ Val=5 };
}
public void MutateReadonly() {
Console.WriteLine(msReadonly.Val); // 5
msReadonly.Mutate(66); // defensive copy!
Console.WriteLine(msReadonly.Val); // still 5!!!
}
public void MutateNormal() {
Console.WriteLine(msNormal.Val); // 5
msNormal.Mutate(66);
Console.WriteLine(msNormal.Val); // 66
}
}
new Test().MutateReadonly();
new Test().MutateNormal();
Try It Online
ValueTuple is a great addition to the framework and language. But there's a reason you'll often hear that [Mutable] structs are evil. In the majority of cases you shouldn't hit these restrictions. If you find yourself falling into this pattern a lot, I suggest moving over to a record, which is a reference type (thus not suffering these issues) and can be reduced to a tuple-like syntax.
Mutable value types are evil, it's hard to see why this prints "Tom" not "Jack":
(string name, int score) student = ("Tom", 100);
(string name, int score) student2 = student;
student.name = "Jack";
Console.WriteLine(student2);
The reason is that you always create a copy. Because it's not obvious you should avoid mutable value types. To avoid that people will fall into that trap the compiler just allows to modify the object directly via properties(like above). But if you try to do it via a method call you get a compiler error "Cannot modify the return value of ... because it is not a variable".
So this is not allowed:
list[0].name = "Jack";
It would create a new copy of the ValueTuple, assigns a value but doesn't use or store it anywhere.
This compiles because you assign it to a new variable and modify it via property:
(string name, int score) x = list[0];
x.name = "Jack"; // Compiles
So it compiles but gives you again a suprising result:
Console.WriteLine(list[0]); // Tom
Read more about it here: Do Not Define Mutable Value Types
I'm creating an anonymous method to generate a radom string.
Before declaring the method, I didn't declare any parameter with the name id.
But I couldn't still define string id = string.Empty; inside the method.
// No parameter within name `id` here...
Func<Task<string>> getIdAsync = null;
getIdAsync = async () =>
{
// string id = string.Empty; // error here. try to use `_id` instead of `id`
string _id = string.Empty;
// logic...
return _id;
};
// _id = ""; // we cann't re-use `_id` here, but `_id` can use `id` below
// `id` is declared after the method
string id = await getIdAsync();
Maybe I misunderstood but I think it should throw me the error when (only in this case):
// declare `id` before
string id = string.Empty;
Func<Task<string>> getIdAsync = null;
getIdAsync = async () =>
{
/*
* A local or parameter named 'id' cannot be declared in this scope
* because that name is used in an enclosing local scope to define a local or parameter
*/
string id = string.Empty;
// logic...
};
id = await getIdAsync();
Can anyone correct me?
This is correct.
You are trying to redeclare the variable id.
The solution will depend on what you are trying to do. If you want to set id then simply remove the type definition;
getIdAsync = async () =>
{
id = string.Empty;
// logic...
};
However, this isn't good practice as id could get overwitten by code outside your async method - unless you actually want this. If this is just a variable local to the scope of the anonymous method then rename it:
getIdAsync = async () =>
{
string localId = string.Empty;
// logic...
};
It's the same as if you were declaring an object of the same name inside an if statement or loop.
The compiler doesn't care what order you declare your local variables with respect to your delegate method because it doesn't know when you're going to use your delegate. So regardless of whether or not you have this statement before or after your delegate declaration, you'll still have a conflict:
string id = await getIdAsync();
It's similar to this question, although that question is dealing with a nested scope rather than an anonymous function, but the reasoning is the same.
The scope of id is the entire function, not just the code after declaration. Since there's no way do disambiguate between the "outer" id variable and the id variable local to the anonymous function, the compiler throws an error to make you specify which variable you are referring to.
Supose that I have this:
CustomObject myCO1 = new CustomObject();
CustomObject myCO2 = myCO1;
CustomObject myCO3 = new CustomObject();
myCO1 = myCO3;
I would like that myCO2 has the same reference than myCO1, to myCO3. Is there any way to do that?
Thank so much.
No, assigning a new reference just copies the reference value into the new variable. When you change it's reference the other variables that references the same place are not affected.
So in your case, myCO2 references the old location of myCO1. And myCO1 and myCO3 are referencing to same location.
It sounds a little bit like you could use a primer on references and values in C#. Specifically: "If x is a variable and its type is a reference type, then changing the value of x is not the same as changing the data in the object which the value of x refers to"
However, let me edit your example for a bit of clarity:-
CustomObject myCO1 = new CustomObject("A");
CustomObject myCO2 = myCO1;
CustomObject myCO3 = new CustomObject("B");
myCO1 = myCO3;
You can see clearly that myCO2 refers to "A", even after you change myCO1 to refer to "B". This is because while a reference refers to another object, it is itself a value.
There are largely two ways you can achieve the outcome you desire.
The first option is using a reference type (rather than a value) to refer to your CustomObject:-
public sealed class Reference<T>
{
public T Value { get; set; }
}
...
Reference<CustomObject> myCO1 = new Reference<CustomObject>()
{
Value = new CustomObject("A");
};
Reference<CustomObject> myCO2 = myCO1;
CustomObject myCO3 = new CustomObject("B");
myCO1.Value = myCO3;
The second option you have is to encapsulate the logic you require in a method and use the ref keyword
I was googling trying to find a way to call Control.DataBindings.Add without using a string literal but getting the property name from the property itself, which I think would be less error prone, at least for my particular case, since I normally let Visual Studio do the renaming when renaming a property. So my code would look something like DataBindings.Add(GetName(myInstance.myObject)... instead of DataBindings.Add("myObject".... So I found this:
static string GetName<T>(T item) where T : class
{
var properties = typeof(T).GetProperties();
if (properties.Length != 1) throw new Exception("Length must be 1");
return properties[0].Name;
}
That would be called, assuming I have a property called One, this way: string name = GetName(new { this.One }); which would give me "One". I have no clue why does it work and whether is safe to use it or not. I don't even know what that new { this.One } means. And I don't know on which case could it happens that properties.Length is not 1.
By the way, I just tested to rename my property One to Two and Visual Studio turned new { this.One } into new { One = this.Two }, which when used with the GetName function gave me "One", which make the whole thing useless since the name I would be passing to Control.DataBindings.Add would be still "One" after renaming the property.
new { this.One } creates an instance of an anonymous type with one Property, which is, because you didn't specify a name, called "One". That's why it works.
if you use new { One = this.Two }, you give the property the name "One". If you would leave out the part "One = ", it would work again.
However, the method you are using might be misunderstood if one does not know how it's intended to be used and if one does not call it using an anonymous type.
There is another way if you don't want to use string literals, here is one of the examples you can find on the web:
http://www.codeproject.com/Tips/57234/Getting-Property-Name-using-LINQ.aspx
No, you do not have to stick to string literals:
public static class ControlBindingsCollectionExtensions
{
public static void Add<T>(this ControlBindingsCollection instance, Expression<Func<T, object>> property)
{
var body = property.Body as UnaryExpression;
var member = body.Operand as MemberExpression;
var name = member.Member.Name;
instance.Add(name);
}
}
Usage:
Control.DataBindings.Add<MyClass>(m => m.MyProperty);
Can I somehow get a reference to the instance I am creating using object initialiser
var x = new TestClass
{
Id = 1,
SomeProperty = SomeMethod(this)
}
"this" should point to the new TestClass instance I'm creating. But it obviously refers the the instance of the class in which this code resides.
I'm not asking if this is a good way to do this.
I'm aware that I can do this like this:
var x = new TestClass {Id= x};
x.SomeProperty = SomeMethod(this);
I have a complicated scenario, in which a reference to the new instance in the object initialiser would make life easier.
Is this possible in any way?
There's no way around it, the C# specification explicitly says that "It is not possible for an object or collection initializer to refer to the object instance being initialized."
As for why it's impossible, I suspect that there's just no nice way to implement it. We want some syntactic sugar equivalent to
var temp = new TestClass();
temp.Id = 1;
temp.SomeProperty = SomeMethod(temp);
x = temp;
We just need a keyword to refer to temp within the initializer, but none is easily available. We can't use this because it already means something outside the initializer. Should SomeProperty = this.SomeMethod(this) be equivalent to temp.SomeProperty = this.SomeMethod(temp) or temp.SomeProperty = temp.SomeMethod(temp)? The second is consistent, but then what happens if we need the first?
We could try to use x, though we can only pick a name if the new object is immediately assigned to a variable. However, we now can't refer to the old value of x inside the initializer, doing the equivalent of temp.SomeProperty = SomeMethod(x).
We could reuse the value keyword from property setters. This sounds good since value already stands in for the missing parameter if you consider a property getter to be syntactic sugar for a set_SomeProperty(value) method. Using it to also refer to the missing variable in the object initializer looks promising. However, we could be creating this object inside a property setter, in which case value is already being used, and we need to be able to do temp.SomeProperty = SomeMethod(value).
It looks like we'll have to create a new keyword just for this purpose, maybe newthis. However, this is a breaking change to the language because any code that has a variable called newthis doesn't work any more. Microsoft generally needs a really good reason to introduce breaking changes, so it's better to forbid access to the object being initialized.
No, you can't use the object initializer to assign the object you're creating somewhere else - that defeats the whole point of the object initializer. The x variable doesn't get assigned until after the object initializer is completed. You'll need to assign the object, then use it in a separate statement.
var x = new TestClass {
Id = 1
};
x.SomeProperty = SomeMethod(x);
Exposing or using an object that hasn't been fully constructed is usually a very bad idea. Consider the following:
class Connection
{
internal string connectionString;
public Connection(ConnectionPool pool, string connectionString) {
this.pool = pool;
//this.connectionString = connectionString; // I moved it because I could.
this.pool.Register(this);
this.connectionString = connectionString;
this.Init();
}
private void Init() { //blah }
}
class ConnectionPool
{
public void Register(Connection c)
{
if ( this.connStrings.Contains( c.connectionString ) ) // BOOM
}
}
This is an extremely contrived example. Things can get a whole lot worse than this. The following was quite an interesting link regarding this issue:
Partially Constructed Objects
var x = new TestClass
{
Id = 1,
SomeProperty = SomeMethod(this)
}
Before the right part of this initialization is evaluated and executed, the reference to the new object is not yet made available to the code. That is done for security purposes, otherwise you could create some deadlock or endless loop with you code.