I am trying to understand c# dynamic. I have an ExpandoObject instance assigned to dynamic variable.
I understand ExpandoObject is implementing IDictionary. But the below assignment fails.
dynamic obj = new ExpandoObject();
obj["Test"] = "TestValue";
Console.WriteLine(obj.Test);
Can someone tell me where i am going wrong?
obj.Test="TestValue";
However the above statement seems to be working fine.
To do that, you need to cast the ExpandoObject to IDictionary<string, object>.
This is normal Expando usage:
obj.Test = "TestValue";
This is string-literal (or string variable) usage:
var d = (IDictionary<string, object>)obj;
d["Test"] = "TestValue";
string s = "Test";
d[s] = "TestValue";
If the interface implementation is explicit, you need to cast to a reference of the interface in order to access the members. I'm guessing this is what has happened here.
Related
Is it possible to programmatically declare a nodejs const from a variable (string?)
let myConsts = ["const1","const2","const3"];
myConsts.forEach(function(label){
defineConst(label,"value"); // I need this function
})
defineConst should define a const, something like the PHP "define" function, but for nodejs
No, you can't really do that in Javascript. For a bit, I thought maybe you could hack it with eval() which is pretty much always the wrong way to solve a programming problem, but even eval() won't introduce a new const variable to the current scope. It will introduce a new var variable to the current scope as in this:
// dynamic variable name
let varName = "test";
// create variable in the current scope
eval("var " + varName + " = 4;");
// see if that variable exists and has the expected value
console.log(test);
But, alas you can't do this with const. You can read more about why here: Define const variable using eval().
Whatever programming problem you are trying to solve can likely be solved in a much better way since you really shouldn't be introducing dynamically named local variables. It's much better to use something like a Map object or a regular object with dynamically named properties in order to keep track of values with dynamic names to them.
If you shared the actual programming problem you're trying to solve (rather than your attempted solution), we could advise further on the best code for that particular problem.
Here's an example of the ability to store dynamically named properties on an object:
let base = {};
// dynamic property name (any string value in this variable)
let varName = "test";
// set property value with dynamic property name
base[varName] = "Hello";
// can reference it either way
console.log(base[varName]);
console.log(base.test);
Or, it can be done using a Map object:
let base = new Map();
// dynamic Map element key (any string value in this variable)
let varName = "test";
// set Map element with dynamic key
base.set(varName, "Hello");
// can reference it either way by the key
console.log(base.get(varName));
console.log(base.get("test"));
One for the gurus, please convince me/us what is going on.
List<ExpandoObject> peopleList = new List<ExpandoObject>();
dynamic expandoObj1 = new ExpandoObject();
expandoObj1.id = 1;
expandoObj1.first = "fred";
expandoObj1.last = "krugger";
peopleList.Add(expandoObj1);
dynamic expandoObj2 = new ExpandoObject();
expandoObj2.id = 2;
expandoObj2.first = "george";
expandoObj2.last = "benson";
peopleList.Add(expandoObj2);
//test access the props
var expObj = expandoObj1;
var name = expObj.first;
var expObj2 = peopleList[0] as dynamic;
var name2 = expObj2.first;
IDictionary<string, object> expObj3 = peopleList[0] as ExpandoObject;
var name3 = expObj3["first"];
var expObj4 = peopleList[0] as ExpandoObject;
//var name4 = expObj4.first; //THIS DOESN'T WORK - ExpandoObject does not contain a definition for 'first' etc...
In all cases, the LEFT-HAND SIDE is a System.Dynamic.ExpandoObject;
Why then, on the 4th case expObj4, i cannot access the property expObj4.first ?
ExpandoObject is a sealed class which stores data in a dictionary. It implements IDynamicMetaObjectProvider interface which provides dynamic behaviour to the classes implementing it. It also implements IDictionary interface which provides dictionary like behaviour to it. It is supposed to be checked and validated at compile time.
dynamic is a type which is not supposed to be checked by the compiler at compile time. It is checked and breaks at runtime. At compile time, a dynamic entity is assumed to support any operation. So, when you say, it is a expandoobject, the field called first does not get appended to object itself.
Check source code of expando object here
https://github.com/Microsoft/referencesource/blob/master/System.Core/Microsoft/Scripting/Actions/ExpandoObject.cs
Think of dynamic behavior like an object. You can put any type there. When you are adding to list, you are adding to list as dynamic, but the inherent type of item being added is ExpandoObject. So, you are able to cast it back to ExpandoObject.
When you say,
expandoObj1.first = "fred";
it is same as saying,
expandoObj1.Add("first", "fred");
When you used
var expObj = expandoObj1;
var name = expObj.first;
you were using expandoObject in dynamic form. So, you were able to access properties directly. When you cast it to ExpandoObject class, you were using actual ExpandoObject class which stores fields in Dictionary, so dot(.) notation does not work.
var expObj4 = peopleList[0] as ExpandoObject;
variable on left hand side is still ExpandoObject, not a dictionary. ExpandoObject exposes its members through collection search.
var name4 = expObj4.Where(t=>t.Key == "first").First().Value;
When you cast it to a dictionary, it works like a dictionary.
IDictionary<string, object> expObj3 = peopleList[0] as ExpandoObject;
var name3 = expObj3["first"];
When you cast it to a dynamic, you can access these keys like they are properties of the class.
Further reference
Dynamically adding properties to an ExpandoObject
This is because the variable expObj4 is declared as ExpandoObject and not as dynamic. This is an important difference.
Try this:
dynamic a = new ExpandoObject();
a.Name = "Test";
This compiles, but the following doesn't:
ExpandoObject a = new ExpandoObject();
a.Name = "Test";
you get this:
CS1061 'ExpandoObject' does not contain a definition for 'Name' and no extension method 'Name' accepting a first argument of type 'ExpandoObject' could be found
The variables you have that are related to this are:
expandoObj1 - dynamic
expandoObj2 - dynamic
expObj1 - dynamic
expObj2 - dynamic
expObj3 - dictionary, but you use dictionary access here, not dot-access
The magic "let's see if we can access the thing at runtime" code of the compiler only kicks in if the expression or variable is dynamic. ExpandoObject is just a type that supports this.
I am trying to use a string ('npcName') as a variable name. So far I have tried casting dialogMap into a DynamicAccess object, but it gives me the error 'Invalid array access' when I try this:
var npcName:String = 'TestNPC';
var casted = (cast Registry.dialogMap:haxe.DynamicAccess<Dynamic>);
var tempname = casted[root.npcName[0].message];
trace(tempname);
'dialogMap' is an empty map which I want to fill like so:
Registry.dialogMap['message'] = root.npcName[0].message;
How can I use npcName, a string, in the above line of code? Is there a way to transform the string into something usable? Any help would be appreciated.
The haxe.DynamicAccess doesn't have array access (like map[key]), but is an abstract type for working with anonymous structures that are intended to hold collections of objects by the string key. It is designed to work with map.get(key) and map.set(key). It is basically a nicer wrapper around Reflect.field and Reflect.setField and does some safety checks with Reflect.hasField.
var variable = "my_key";
var value = 123;
var dynamicMap = new haxe.DynamicAccess<Dynamic>();
dynamicMap.set(variable, value);
I'm noticing you are doing very much cast and dynamic, so untyped code, which is a bit of contradiction in a typed language. What is the actual type of dialogMap?
Not sure you are aware of it but, Haxe has its own maps, which are fully typed, so you don't need casts.
var map = new Map<String, Int>();
map[variable] = value;
I think this article helps understanding how to work with dynamic (untyped) objects.
Tip; for testing such small functionalities you can doodle around on the try.haxe site : http://try.haxe.org/#4B84E
Hope this helps, otherwise here is some relevant documentation:
http://api.haxe.org/haxe/DynamicAccess.html
https://haxe.org/manual/std-reflection.html
https://haxe.org/manual/types-dynamic.html
http://code.haxe.org/category/beginner/string-variable-reflection.html
I get null value of instrumentRented
when I run
public class RentalAgreement
{
private MusicalInstrument instrumentRented;
public RentalAgreement(Customer renter,
RentalDate dateRented,
MusicalInstrument instrumentRented){
customer = renter;
rentalDate = dateRented;
instrumentRented = instrumentRented;
How to initialize MusicalInstrument reference in RentalAgreement?
Use this.instrumentRented = instrumentRented;
Since the parameter has the same name as the field attribute, you need to prefix with an explicit this to specify the scope.
You must instantiate a class with the new operator.
So somehwere in your code must do
instrumentRented = new MusicalInstrument();
before you access it. After you have done this you can execute functions from that class.
instrumentRented.doSomething();
In the above code you seem to pass it in in the constructor, so this means the caller has to instantiate it.
However I would advise to adopt a naming convention where you can see if a variable is a class member or a local variable. In your above code the local variable has the same name as the parameter, so it will not be set as a member variable, instead you assign it to itself. You might get a warning about this, depending on the evironment (Not sure about this but Eclipse definitely warns somethign like this). This is called shadowing, so what you need to do is:
this.instrumentRented = instrumentRented;
Scenario:
I have x number of classes. Lets say 10; Each class does different UI Functions. When a user loads a file, that extension tells the program the classname to load; but it's in the form of a string.
Is there anyway to pass a string off as a classname? Something to the effect of.
var classname = "Booger";
var nose = new classname(){ //classname really means "Booger"
//Do Operation
}
You can reflect a type by name using var t = Type.from_name(classname);, however, this works on all types, including enums and structs and it might be the type Type.INVALID. You should probably do some checks, like t.is_a(typeof(MyParentClass)).
You can then instantiate a copy using var obj = Object.new(t);. The whole thing would look like:
var classname = "Booger";
var t = Type.from_name(classname);
if (t.is_a(typeof(MyParentClass)))
return Object.new(t);
else
return null;
It's also worth noting that the run-time type names have the namespace prepended, so you might want to do "MyNs" + classname. You can check in either the generated C or doing typeof(MyClass).name().
I've had the same problem as the OP in regards to getting an assertion error against null. If you take a look at the Glib documentation (in C) it mentions you have to register your class by actually specifying the class name first before you can actually use a string representation of your class name.
In other words you have to use your class first BEFORE you can instantiate a copy of your class with Glib.Type.from_name ("ClassName").
You can use your class first by instantiating a class instance or by getting type information for your class.
var type = typeof (MyClass);
var type_from_string = Type.from_name ("MyClass");
Furthermore, when you use Object.new to create a class there are two things you need to be aware of:
1) You need to cast the return value to get your specific class or base class.
var instance_of_my_class = Object.new (type) as MyClass;
2) Constructors for your class will no longer be called (I don't why). You will need to use the GObject style constructor inside your class:
construct {
pizza = 5;
}