How do I make the complex structure of an anonymous object public inside a dynamic object?
Anonymous objects are flagged as internal, so I'm looking for a creative way to work around this.
// This is the library I control
public void SendObject() {
var anonymous = new {
Text = "Test",
SubItem = new {
SubText = "Bla",
SubSub = new {
SubSubText = "Baha"
}
}
};
}
dynamic dyn = ExposeAnonymous(anonymous); // Perform voodoo
var result = ExternalLibrary.GetSpecialProperty(dyn);
// External library I don't control
public object GetSpecialProperty(dynamic dyn) {
return dyn.SubItem.SubSub.SubSubText;
}
The problem is when sending the dynamic to other external libraries, that I don't control, you get an error like:
'object' does not contain a definition for 'SubItem'.
The problem is when sending the dynamic to other libraries,
... And there's the rub. Anonymous types are declared as internal by the C# compiler, which means that other assemblies don't have access to them.
Either stop using anonymous types, or use [InternalsVisibleToAttribute] to make the type visible to other assemblies. Within the assembly containing the type which creates the instance of the anonymous type, use:
[InternalsVisibleTo("ExternalLibrary")]
(I'd actually expect the issue to be on SubItem rather than SubSub...)
Anonymous types are internal and the DLR does the same accessibility analysis at run-time that the compiler does at compile-time. So you cannot access the anonymous type's members from another assembly using dynamic.
One option might be to use ExpandoObject:
dynamic a = new ExpandoObject();
a.Text = "Test";
a.SubItem = new ExpandoObject();
a.SubItem.SubText = "Blah";
a.SubItem.SubSub = new ExpandoObject();
a.SubItem.SubSub.Text = "Baha";
This is kind of ugly so, you could keep the anonymous type and use a helper method to recursively convert to an ExpandoObject:
public static dynamic ConvertToExpando(object obj)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach(var pi in obj.GetType().GetProperties())
{
// there doesn't seem to be a way to know if it is an anonymous type directly. So I use IsPublic here.
if (pi.PropertyType.IsPublic)
{
expando[pi.Name] = pi.GetValue(obj);
}
else
{
expando[pi.Name] = ConvertToExpando(pi.GetValue(obj));
}
}
return expando;
}
This does the "Perform voodoo" that you need.
With my opensource framework ImpromptuInterface (in nuget) you can create complex expando graphs with an inline syntax.
dynamic New = Builder.New<ExpandoObject>();
dynamic dyn = New.Obj(
Text: "Test",
SubItem: New.Obj(
SubText: "Bla",
SubSub: New.Obj(
SubSubText: "Baha"
)
)
);
Related
I have object of some type known at runtime and I read and deserialize this object from database. It works. Now I would like to add it to some list:
private static List<T> generateList<T>()
{
List<T> lst = new List<T>();
return lst;
}
private void readObjects(System.Type objType)
{
var methodInfo = typeof(My.Serializator).GetMethod("DeserializeDb");
var genericMethod = methodInfo.MakeGenericMethod(objType1);
List<curType> currentList= generateList<curType>();
// ...read stream from database and convert it to object
while (_rs.Read())
{
var _objItem = genericMethod.Invoke(null, new[] { _streamedData });
currentList.Add(_objItem);
}
}
It won't work. The error is:
curType is a variable but is used like a type.
If I change list to:
List<object> currentList = new List<object>();
it will work. But can i do this with generics(T) instead of object type?
You can easly create type of list you want via Activator, then cast to IList and use it:
private IList readObjects(System.Type objType)
{
var listType = typeof(List<>).MakeGenericType(curType);
var list = (IList)Activator.CreateInstance(listType);
// ...
while (_rs.Read())
{
// ...
list.Add(_objItem);
}
}
list will be instance of List<YorActualType>.
Update
When you declaring your method with generic arguments, it assumes you provide type info during compile time. Otherwise you need to use reflection.
Since you providing type info in run time (curType can hold any type info), compiler does not know what exactly type will be used, and you cannot declare your method to return something concrete. Only abstractions allowed.
Let's me show it on slightly insane but demonstrative example:
var types = new [] { typeof(int), typeof(string) };
var rand = new Random();
var list = readObjects(types[rand.Next(0,2)];
Until the very last moment even you will not know what exactly type of list will be created. Compiler does not know too. Compiler will never know what exactly type should be used if you not provide him with you types. When you use Type it only tells compiler that some regular parameter with type Type will be passed into the method in run time. There is no data to infer a type during compile time. That data can be passed only via generic type parameters.
So, there is several ways you can follow:
Provide exact types you need at compile time
private List<T> readObjects<T>()
{
var objType = typeof(T);
var list = new List<T>();
// rest of code....
}
Use reflection and base types
private IList readObjects(Type objType)
{
// rest of code with Activator and so on
}
And later usage depends on your needs.
If you know what type you going to use, simply convert:
var list = (IList<MyType>)readObjects(typeof(myType));
But I guess in that case better use way #1 with generic argument.
Otherwise you going to use reflection. Or some base classes, interfaces and so on. It depends on what exactly task you going to solve.
P.S. You can read more about generic types on MSDN.
Code example:
void Foo(params object[] objects)
{
var entries = new List<IEntry>();
foreach(var o in objects)
{
var entry = new Entry<o.GetType()>(); // this doesn't work
entries.Add(entry);
}
...
}
Foo("hello", 5); // should fill entries with Entry<string> and Entry<int>
Why is that not possible? I guess I need to work with reflection instead? How to do that properly AND performant?
You just can't use C# generics the way you're trying to do in your snippet.
In order to use [C#] generics, the actual object type must be known at compile time.
You're trying to dynamically pass the object type as a type parameter. This is simply not possible.
Edit
Yes, it is possible to dynamically create generic objects using reflection. After all, generics is implemented both as a compile-time C# construct and as a .NET framework feature (as opposed to, say, Java, where it is only a compile-time feature based on Type Erasure). So, in .NET, through reflection, it is possible to implement the latter "bypassing" the former (which, again, would be impossible in Java).
But the OP clearly does not need that.
After all, entries is a List<IEntry>. IOW, the entries container does not "know" the concrete type of its elements (since it is bound to an interface). So, if each element to be add already implements IEntry, then this would be enough:
void Foo(params IEntry[] objects)
{
var entries = new List<IEntry>();
foreach(var o in objects)
{
entries.Add(o);
}
...
}
OTOH, if those objects do not implement IEntry, then the OP just need a pure, ordinary, old-school list of untyped objects:
void Foo(params object[] objects)
{
var entries = new List<object>();
foreach(var o in objects)
{
entries.Add(o);
}
...
}
So using reflection in order to dynamically create a generic container, even if possible, seems to be overkill for this particular use case.
You can do it with reflection
var entryType = typeof(Entry<>);
Type[] typeArgs = { o.GetType() };
var genericType = entryType.MakeGenericType(typeArgs);
IEntry entry = (IEntry)Activator.CreateInstance(genericType);
You need a function of the form:
Func<Type, IEntry>
I would suggest adding a static function to the parent of Foo like this:
public static IEntry Make(Type type)
Inside that function, feel free to add whatever code makes sense to you:
if (type == typeof(string))
{
return new StringEntry(); //Obviously some special logic based on the type.
}
else
{
//Default logic
return (IEntry) Activator.CreateInstance(typeof(Entry<>).MakeGenericType(type));
}
object o = new { foo = 1 };
Is there any other way to add properties to the object?
Neither o["foo"] = 1 nor o.foo = 1 seems to be working.
And if i type
var o = new { foo = 1 };
Why is the type of var an "AnonomuysType" and not object?
You can't just add properties to compiled types. The only time that will work as properties (well, kind-of) is if you are using the dynamic API, and the object is something that supports dynamic properties, for example:
dynamic obj = new ExpandoObject();
obj.Foo = 123;
obj.Bar = "abc";
however, those values are not available via reflection. An easier approach may be something that behaves as a dictionary:
var obj = new Dictionary<string,object>();
obj["Foo"] = 123;
obj["Bar"] = "abc";
there are more discoverable, via the dictionary API. Re your final question: when you do:
var o = new { foo = 1 };
the new { foo = 1 } causes the compiler to generate an anonymous type, which is a regular C# class with read-only properties and an unpronounceable name. This can be referred to as object (it is a regular class, after all), but when you use var the compiler uses the known type (with the horrible name), which gives you access to the properties (.foo in this case).
All objects inherit from Object even an anonymous type(see this question). You can see that here:
object o = new { foo = 1 };
if (o is object)
Console.Write("Yes, i'm an object!");
else
Console.Write("no, no, i'm not an object!");
You cannot add properties to an anonymous type, only when you you create it.
You could use a Dictionary<string, object> instead:
var o = new Dictionary<string, object>();
o.Add("Foo", 123);
o.Add("Bar", "abc");
Now you can access them in this way:
object bar = o["Bar"]; // "abc"
I'd like to access the value of a dynamic c# property with a string:
dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };
How can I get the value of d.value2 ("random") if I only have "value2" as a string? In javascript, I could do d["value2"] to access the value ("random"), but I'm not sure how to do this with c# and reflection. The closest I've come is this:
d.GetType().GetProperty("value2") ... but I don't know how to get the actual value from that.
As always, thanks for your help!
Once you have your PropertyInfo (from GetProperty), you need to call GetValue and pass in the instance that you want to get the value from. In your case:
d.GetType().GetProperty("value2").GetValue(d, null);
public static object GetProperty(object target, string name)
{
var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
return site.Target(site, target);
}
Add reference to Microsoft.CSharp. Works also for dynamic types and private properties and fields.
Edit: While this approach works, there is almost 20× faster method from the Microsoft.VisualBasic.dll assembly:
public static object GetProperty(object target, string name)
{
return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}
Dynamitey is an open source .net std library, that let's you call it like the dynamic keyword, but using the a string for the property name rather than the compiler doing it for you, and it ends up being equal to reflection speedwise (which is not nearly as fast as using the dynamic keyword, but this is due to the extra overhead of caching dynamically, where the compiler caches statically).
Dynamic.InvokeGet(d,"value2");
The easiest method for obtaining both a setter and a getter for a property which works for any type including dynamic and ExpandoObject is to use FastMember which also happens to be the fastest method around (it uses Emit).
You can either get a TypeAccessor based on a given type or an ObjectAccessor based of an instance of a given type.
Example:
var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");
var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");
dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");
var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");
typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");
typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");
Much of the time when you ask for a dynamic object, you get an ExpandoObject (not in the question's anonymous-but-statically-typed example above, but you mention JavaScript and my chosen JSON parser JsonFx, for one, generates ExpandoObjects).
If your dynamic is in fact an ExpandoObject, you can avoid reflection by casting it to IDictionary, as described at http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx.
Once you've cast to IDictionary, you have access to useful methods like .Item and .ContainsKey
The GetProperty/GetValue does not work for Json data, it always generate a null exception, however, you may try this approach:
Serialize your object using JsonConvert:
var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));
Then access it directly casting it back to string:
var pn = (string)z["DynamicFieldName"];
It may work straight applying the Convert.ToString(request)["DynamicFieldName"], however I haven't tested.
d.GetType().GetProperty("value2")
returns a PropertyInfo object.
So then do
propertyInfo.GetValue(d)
To get properties from dynamic doc
when .GetType() returns null, try this:
var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
This is the way i ve got the value of a property value of a dinamic:
public dynamic Post(dynamic value)
{
try
{
if (value != null)
{
var valorCampos = "";
foreach (Newtonsoft.Json.Linq.JProperty item in value)
{
if (item.Name == "valorCampo")//property name
valorCampos = item.Value.ToString();
}
}
}
catch (Exception ex)
{
}
}
Some of the solutions were not working with a valuekind object that I obtained from a json string, maybe because I did not have a concrete type in my code that was similar to the object that I would obtain from the json string, so how I went about it was
JsonElement myObject = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(jsonStringRepresentationOfMyObject);
/*In this case I know that there is a property with
the name Code, otherwise use TryGetProperty. This will
still return a JsonElement*/
JsonElement propertyCode = myObject.GetProperty("Code");
/*Now with the JsonElement that represents the property,
you can use several methods to retrieve the actual value,
in this case I know that the value in the property is a string,
so I use the GetString method on the object. If I knew the value
was a double, then I would use the GetDouble() method on the object*/
string code = propertyCode.GetString();
That worked for me
In .Net core 3.1 you can try like this
d?.value2 , d?.value3
Similar to the accepted answer, you can also try GetField instead of GetProperty.
d.GetType().GetField("value2").GetValue(d);
Depending on how the actual Type was implemented, this may work when GetProperty() doesn't and can even be faster.
In case you have a dynamic variable such as a DapperRow for example you can first build up an ExpandoObject, then cast the Expando into an IDictionary<string, object>. From then on, getting a value via the name of a property is possible.
Helper method ToExpandoObject:
public static ExpandoObject ToExpandoObject(object value)
{
IDictionary<string, object> dapperRowProperties = value as IDictionary<string, object>;
IDictionary<string, object> expando = new ExpandoObject();
if (dapperRowProperties == null)
{
return expando as ExpandoObject;
}
foreach (KeyValuePair<string, object> property in dapperRowProperties)
{
if (!expando.ContainsKey(property.Key))
{
expando.Add(property.Key, property.Value);
}
else
{
//prefix the colliding key with a random guid suffixed
expando.Add(property.Key + Guid.NewGuid().ToString("N"), property.Value);
}
}
return expando as ExpandoObject;
}
Sample usage, I have marked in bold the casting which gives us access in the example, I have marked the important bits with the ** letters:
using (var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
foreach (var dynamicParametersForItem in dynamicParametersForItems)
{
var idsAfterInsertion = (await connection.QueryAsync<object>(sql, dynamicParametersForItem)).ToList();
if (idsAfterInsertion != null && idsAfterInsertion.Any())
{
**var idAfterInsertionDict = (IDictionary<string, object>) ToExpandoObject(idsAfterInsertion.First());**
string firstColumnKey = columnKeys.Select(c => c.Key).First();
**object idAfterInsertionValue = idAfterInsertionDict[firstColumnKey];**
addedIds.Add(idAfterInsertionValue); //we do not support compound keys, only items with one key column. Perhaps later versions will return multiple ids per inserted row for compound keys, this must be tested.
}
}
}
In my example, I look up a property value inside a dynamic object DapperRow and first convert the Dapper row into an ExpandoObject and cast it into a dictionary property bag as shown and mentioned in other answers here.
My sample code is the InsertMany method for Dapper extension I am working on, I wanted to grab hold of the multiple ids here after the batch insert.
Use dynamic with Newtonsoft.Json.JsonConvert.DeserializeObject:
// Get JSON string of object
var obj = new { value1 = "some", value2 = "random", value3 = "value" };
var jsonString = JsonConvert.SerializeObject(obj);
// Use dynamic with JsonConvert.DeserializeObject
dynamic d = JsonConvert.DeserializeObject(jsonString);
// output = "some"
Console.WriteLine(d["value1"]);
Sample:
https://dotnetfiddle.net/XGBLU1
Consider the following:
// select a subset of the DataTable
var subset = DataTable.Where(...).Select(row => new
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
// or create a new object
var subset = new {
Id = 1,
Name = "something random",
Email = "name#domain.tld"
};
Is there any way to use the subset variable as a parameter to a method, without it being cast as a plain Object? Can you somehow carry the auto-generated type of the variable?
I am trying to avoid having to create new classes every time I want to pass LINQ subsets to methods.
Random generic approaches are welcome.
No, passing anonymous types about isn't generally a good idea because you lose the type information*. You should create a concrete type and use that instead.
var subset = DataTable.Where(...).Select(row => new SomeType
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
Alternatively you can use the Tuple type if you are using .NET 4. This is a simple way to create "disposable" types and still get some type-safety.
*Actually there is a workaround, but I regard it is an ugly hack and would advise that you don't do it.
If I need to do this, I use resharper's "Replace Anonymous Type With Named Class" refactoring option. Then you have an appropriate named type to expose over the API, and you haven't had to do any work. This also gives you options to create it immutable (like anonymous types) or mutable, nested vs top-level, etc.
BTW, I don't recommend struct here (from the question).
Another option is to pass the behaviour into the method - i.e. an Action<int,string,string> callback - then do something like:
foreach(item in query) callback(item);
However, I don't like this as it is not obvious that there is a likely error in:
DoSomething(args, (id, email, name) => Email(To: email, Subject: name));
(the error being that it should probably be (id, name, email), if you see what I mean)
You can use a generic method:
public static void Foo<T>(T item)
{
// Do whatever
}
Then if you call
Foo(subset);
the compiler will infer T for you. Whether or not that actually helps you is another matter... it depends on what the method is meant to do. Obviously Foo can't refer to Id, Name, Email etc.
In general, if multiple methods should know about the same members, then you should use a named type. The usual case for passing them to generic methods is where the method really doesn't care about what type is involved, such as in LINQ.
I've made a feature request for C# 5 that we should be able to create types which have all the same features as anonymous types (immutability, equality, hash code generation, ToString dumping) but for simple named types. We'll see if it actually happens...
Anonymous Types don't provide much help outside of the context they where created.
If you need to pass an Anonymous Type to a method, either this method is very generic like (Example)
void PrintAllObjectProperties(object obj);
witch you would use reflection to do the work, or you are doing something wrong.
Here's what I came up with...
Extension method on Object:
public static class ObjectExtensions
{
/// <summary>
/// Cast Object to anonymous type.
/// E.G.: new Object().ToAnonymousType(new { Property = new Type() });
/// </summary>
public static T ToAnonymousType<T>(this Object o, T t)
{
return (T)o;
}
}
Usage:
public void HandleAnonymousTypeAsParameter(Object o)
{
var anonymousType = o.ToAnonymousType(new
{
Id = new Int32(),
Foo = new String(),
Bar = new String()
});
// ... You can do this in even less characters:
var anonymousType = o.ToAnonymousType(new { Id = 0, Foo = "", Bar = "" });
}
HandleAnonymousTypeAsParameter(new
{
Id = 1,
Foo = "foo",
Bar = "bar"
});
Credits goes to John Skeet and Thomas P.