I search on Google but the result is misunderstand.
This is a code flow:
public class Store
{
public dynamic GetList(List<string> propertyFields)
{
// Body here
// for in list propertyFields. Such as: ["Code", "Name", "Address", ...]
// dynamic result = ...
// result.Code = "value", result.Name = "abc", result.Address = "homeless", result.... = "..."
// return result
}
}
Method returns a dynamic object.
propertyFields is a list of fields name. When I pass 4 strings in list, dynamic has 4 property fields with value (later).
When I call this method:
Store store = new Store();
var rs = store.GetList(["Code","Name","Address"])
Console.WriteLine(rs[0].Code)
That is my point.
My question: Is it possible to do that in C#?
You have confused dynamic, which is a compiler feature that means "defer type analysis of uses of this object until runtime" with ExpandoObject which means "an object that can have properties added at runtime".
It is an understandable confusion; many languages that lack static type checking also have expando objects.
You need to take a look to ExpandoObject class.
Here is more detailed answer to your question:
Dynamic class creation
What are the true benefits of ExpandoObject?
Related
My HTTP response data includes a field that is structured as a Dictionary<string,object>. Accessing the Object in debug mode shows me what I expect to see, i.e.
ValueKind = Object:
{ "someProp" : "someValue",
"anotherProp" : "anotherValue" .. }
=> the object therefore contains the large json formatted data that I need to access. However, I can't figure out how to do that .. The commonly suggested method returns null:
var myValue = Odata.GetType().GetProperty("someProp").GetValue(Odata, null);
which makes me think there is something I should be serializing which I am not and regardless how much I tried, I can't find any methods to work.
Here is some more info, if it is not enough I will add whatever is needed. I broke it down a lot to try to show clearly what I am getting each step):
// assume Odata = the Object value from the dictionary
//perform reflection
var type = Odata.GetType() // this gives System.Text.json.JsonElement
//get type properties at runtime
var props = type.GetProperties()
//returns array of 2 :
//System.Text.Json.ValueKind and System.Text.json.JsonElement
//attempting to access my properties in the JsonElement
var someValue= type.GetProperty("someProp").GetValue(Odata,null) //this gives null
I don't necessarily want an exact solution, but pointing me to the right place to read would also be very useful!
When you do GetType().GetProperty() you're using the GetProperty() method from Type class, you're using reflection. You want to use the GetProperty method of JsonElement class instead:
var myValue = Odata.GetProperty("someProp").GetString();
Story
I'm trying to write a generic method which combines property names, types and content value to generate a unique string for the value held by the object passed.
The idea is to generate a unique SHA3-512 Hash based on the generated string sequence which can be used to compare objects on generic bases by looking at their content.
Example
Let's say we have a class like this ...
class MyClass {
private Int32 Id = 5;
public String Name = "some string";
protected DateTime CreateDate = DateTime.Parse("2017-08-21 15:00:07");
}
... and the mentioned method to generate the unique string
static String GetContentString<T>(T obj) where T : class {
...
}
In theory this should work somewhat like this:
var myObj = new MyClass();
var uniqueContentString = GetContentString(myObj);
Console.WriteLine(uniqueContentString);
>>> Id:Int32:5$Name:String:some string$CreateDate:DateTime:2017-08-21 15:00:07
Problem
I'm having difficulties building the GetContentString Method. This is what I have already:
Object obj = ... // given object
Type type = obj.GetType();
IList<PropertyInfo> propertyInfos = type.GetProperties().Where(x => x.CanRead).ToList(); // Marker #2
StringBuilder sb = new StringBuilder();
foreach (PropertyInfo pi in propertyInfos)
{
sb.Append(pi.Name);
sb.Append(":");
sb.Append(pi.PropertyType.Name);
sb.Append(":");
sb.Append(pi.GetValue(obj) ?? "[ISNULL]"); // Marker #1
sb.Append(":");
}
return sb.ToString();
I tried running the method for a few different types of values like "some string" or 1234 (Int32) and ran into a few issues.
Given a string, the method call throws an exception of type System.Reflection.TargetParameterCountException and the message Parameter count mismatch at #1. I found out that an optional index can be passed to an overloaded version of pi.GetValue(..) which then returns one of the single letters. But how do you know when to stop? If you call an index which doesn't exist it throwns an exception of the type System.Reflection.TargetInvocationException. How do you get the value of a string object using reflection?
Given an integer value, the method call doesn't find any properties at #2. Which brings up the question of how to get the value of an integer object using reflection?
And also some general questions; do you guys think this is a good approach to get a unique string? Is reflection the way to go here? Is it even possible to write a generic solution to this problem?
Without looking at reflection, how about JSON serialization with something that .net framework is able to ?
Reflection isn't something extremely fast and you'll run into issues at the first unhandled exception.
Then, you should do that recursivly if your objects can contains complex properties, wich is not a problem with json serialization !
Imagine I have created an object name of oItem. And this has a lot of attributes such as ItemCode, ItemName.....(around 300). And I want to assign new values to some of these attributes which were selected by the user of my application. User will give these attribute names as strings.
Ex: string attribute1 = "ItemCode".
Now what I want to do is assign a value to this attribute like:
oItem.attribute1 = "01234";
Is there a way to do something like this? I know you can convert a c# function call to a string. Therefore I think this should be possible too. Any help would be highly appreciated. Thanks!
UPDATE: This is a part of my SAP add-On. So these attributes are from a database table. The hard part is user can add more columns(user defined fields) to this which increase the number of attributes as well as I only know the original 300 attributes.
If you have 300 properties you should really refactor this class. I think that you can use a Dictionarystring, string> in this case.
Dictionary<string, string> Items = new Dictionary<string, string>
{
{"attribute1", "01234"}, {"attribute2", "56789"}, {"attribute3", "76543"}, // ...
};
You can access the values very efficiently:
string attribute1 = Items["attribute1"];
or add/modify them in a similar way:
Items["attribute4"] = "23456";
To make it easier for you there's a "dynamic" keyword in C#.
A sample from msdn dynamic object article:
// The class derived from DynamicObject.
public class DynamicDictionary : DynamicObject
{
// The inner dictionary.
Dictionary<string, object> dictionary
= new Dictionary<string, object>();
// This property returns the number of elements
// in the inner dictionary.
public int Count
{
get
{
return dictionary.Count;
}
}
// If you try to get a value of a property
// not defined in the class, this method is called.
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
string name = binder.Name.ToLower();
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
return dictionary.TryGetValue(name, out result);
}
// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
dictionary[binder.Name.ToLower()] = value;
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
}
class Program
{
static void Main(string[] args)
{
// Creating a dynamic dictionary.
dynamic person = new DynamicDictionary();
// Adding new dynamic properties.
// The TrySetMember method is called.
person.FirstName = "Ellen";
person.LastName = "Adams";
// Getting values of the dynamic properties.
// The TryGetMember method is called.
// Note that property names are case-insensitive.
Console.WriteLine(person.firstname + " " + person.lastname);
// Getting the value of the Count property.
// The TryGetMember is not called,
// because the property is defined in the class.
Console.WriteLine(
"Number of dynamic properties:" + person.Count);
// The following statement throws an exception at run time.
// There is no "address" property,
// so the TryGetMember method returns false and this causes a
// RuntimeBinderException.
// Console.WriteLine(person.address);
}
}
// This example has the following output:
// Ellen Adams
// Number of dynamic properties: 2
It is really easy to do this for SAP B1 addon using c# with the provided library functions. Simply include using SAPbobsCOM;
And then you can do it as follows
yourobject.UserFields.Fields.Item("attribute").Value = "value";
I have a function that returns an anonymous type like so (simplified for illustrative purposes)...
public object GetPropertyInfo()
{
return new {
PropertyName = "Foo",
Value = "Laa"
};
}
When I do this...
dynamic pi = GetPropertyInfo();
Console.WriteLine(pi);
It outputs this (the same as if I did '?pi' in the immediate window)...
{ PropertyName = "A", Value = 44 }
PropertyName: "A"
Value: 44
But if I try doing this...
string propertyName = pi.PropertyName;
...it compiles but throws a runtime exception saying
Exception thrown: 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' in System.Core.dll
Additional information: 'object' does not contain a definition for 'PropertyName'
What gives? What am I missing here?
The problem is that anonymous types are internal, which means that you can't access their properties with dynamic property accessors from projects other than the one they were created in. The dynamic binding treats them as the closest public inherited type it knows about--object.
To fix this, you can declare a public type to represent the values you're expecting to find in your anonymous type. This is probably a good idea anyway, since you're clearly expecting to consume the returned properties in other parts of your code. Using a declared type also enables you to maintain type-safety, avoiding the need for dynamic entirely.
If you absolutely must use dynamics here, the next best option is probably to change your AssemblyInfo.cs file to make internal properties accessible to the project you're trying to access them from:
[assembly:InternalsVisibleTo("MyOtherProject")]
Edit
According to your edit. Apparently you are not required dynamic at all as there are no dynamic properties. Just create a concrete type with your predefined properties. It's better to avoid dynamic when possible anyway.
Old Answer
You need to use an ExpandoObject. Reference here.
In fact, GetPropertyInfo() should return an ExpandoObject.
dynamic foo = this.GetPropertyInfo();
string i = foo.MyPropertyName;
private ExpandoObject GetPropertyInfo()
{
dynamic obj = new ExpandoObject();
obj.PropertyName = "MyPropertyName";
obj.PropertyType = "MyPropertyType";
return obj;
}
The ExpandoObject class enables you to add and delete members of its
instances at run time and also to set and get values of these members.
This class supports dynamic binding, which enables you to use standard
syntax like sampleObject.sampleMember instead of more complex syntax
like sampleObject.GetAttribute("sampleMember").
Also, you can use System.Reflection
object D = GetPropertyInfo();
Type t = D.GetType(); // get object's type
PropertyInfo p = t.GetProperty("PropertyName"); // look up for the property:
object P = p.GetValue(D, null); // get the value
Fiddle demo
I have a function that returns an anonymous type which I want to test in my MVC controller.
public JsonResult Foo()
{
var data = new
{
details = "something",
more = "More"
};
return Json(data);
}
I want to verify the data I get from the Foo function, What I'm doing now is getting the data type and get it's properties values with reflection.
[Test]
public void TestOne()
{
var data = _controller.Foo().Data;
var details = data.GetType().GetProperty("details").GetValue(data, null);
var more = data.GetType().GetProperty("more").GetValue(data, null);
Assert.AreEquals("something", details);
Assert.AreEquals("More", more);
}
Is there a simple way similar to this to check the anonymous properties?
[Test]
public void TestTwo()
{
var data = (dynamic) _controller.Foo().Data;
var details = data.details; // RunTimeBinderException object does not contain definition for details
var more = data.more;
Assert.AreEquals("something", details);
Assert.AreEquals("More", more);
}
Anonymous objects are internal, which means their members are very restricted outside of the assembly that declares them. dynamic respects accessibility, so pretends not to be able to see those members. If the call-site was in the same assembly, I expect it would work.
Your reflection code respects the member accessibility, but bypasses the type's accessibility - hence it works.
In short: no.
This blog had a working answer: http://blog.jorgef.net/2011/06/converting-any-object-to-dynamic.html - Thanks #Jorge-Fioranelli.
public static class DynamicExtensions {
public static dynamic ToDynamic(this object value) {
IDictionary<string, object> expando = new ExpandoObject();
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
expando.Add(property.Name, property.GetValue(value));
return expando as ExpandoObject;
}
}
Anonymous type is a regular static type in .NET, it's just that you do not give it a name (a compiler, however, does). That's why casting it to dynamic will not work. However, if you have control over Foo(), you can construct and return a dynamic object instead of anonymous, and then your code is going to work. This should do the trick:
dynamic JsonResult Foo() {
dynamic data = new ExpandoObject();
data.details = "something";
data.mode = "More";
return Json(data);
}
As suggested by #TrueWill and #Marc Gravell, who also referred to this blog post
Since this is for unit testing, you could use InternalsVisibleTo. See Anonymous Types are Internal, C# 4.0 Dynamic Beware! Thanks to #MarcGravell for pointing out that anonymous objects are internal!
Bottom line: Set up an [assembly: InternalsVisibleTo("foo")] mapping if you want to share an anonymous object from one assembly to another. In the OP case, it would be a matter of setting this in the MVC controller project, referring to the test project. In my specific case, the other way around (since I'm passing an anonymous object from my test project into the "production code" project).
The easiest way in that "other project" to be able to use it is definitely to cast it to dynamic and then just use the properties like normal. It does work, no problems whatsoever.
So, bottom line: I feel that Marc Gravell's answer is slightly incorrect; this can clearly be done
(iff the projects in question are modifiable by you, so you can set up the InternalsVisibleTo mapping accordingly, and this does not pose a problem for whatever other reason).
You can use NewtonSoft or the Asp.net MVC libraries:
var data = Json.Decode(Json.Encode(_controller.Foo().Data));
var data=JsonConvert.DeserializeObject<Dictionary<string,object>>(JsonConvert.SerializeObject((_controller.Foo().Data))