C# has a great params keyword for passing arbitary number of arguments to functions such as String.Format(). But what if I need to pass named parameters (key-value pairs)? What is the best method for that (I'm looking for a short syntax on callers side)?
func(new Dictionary<string, object> { { "param1", val1 }, { "param2", val2 } } is too cumbersome
func(param1 => val1, param2 => val2) and func(new { param1 = val1, param2 = val2 }) but it looks like language abuse and those features are not supposed to be used like that
on dynamic objects I can parse optional parameters names func(param1: val1, param2: val2) which looks like a good solution but is doesn't work for common object methods
you can create some overloads of function. when there are only 1 or 2 parameters on caller-side, those overloads will encapsulate usage of Dictionary
public void Foo (string paramA, object valueA)
{
this.Foo(new Dictionary<string, object> { { paramA, valueA } });
}
public void Foo (string paramA, object valueA, string paramB, object valueB)
{
this.Foo(new Dictionary<string, object> { { paramA, valueA },{ paramB, valueB } });
}
public void Foo (Dictionary<string, object> args)
{
}
If.NET Framework 4 is ok for you, maybe you can use Tuple
void MyFunction(params Tuple<string, int>[] p)
// Example of call
MyFunction(Tuple.Create("pipo", 1), Tuple.Create("poil", 2)); // Matthew Mcveigh
[Edit]
An other way is to cast everybody as object:
void MyFunction(params object[] data)
{
for (var i = 0; i < data.Length; i += 2)
{
var s = (string) (data[i]);
var k = (int) (data[i+1]);
...
}
}
...
// Example of call
MyFunction("pipo", 1, "poil", 2);
params can only be used once. And the type of the data passed by it is unique. With params you can either:
Use the generic type object
Use a tuple
There is no way that you can just pass an arbitrary long list of parameters with different type.
If you admit that, for preserving the type, you have to put your stuffs in some new container the cheapest way is to use two of them.
void MyFunction(string[] names, int[] values)
// Example of call
MyFunction(new[] {"pipo", "poil"}, new[] {1, 2});
One example of a library with an API like the one you want to make is iTween. It's well known in some game dev circles, so the practice can't be considered that bad.
It uses a helper function to create a hash table that's passed to the main function. You call it like this:
iTween.MoveTo(camera, iTween.Hash("path", iTweenPath.GetPath("CamPath"), "looktarget", player,
"looktime", 0f, "speed", 2, "easetype", iTween.EaseType.linear));
It's open source, so you can look it if you want to search for "itween.cs". The helper function takes a params object[] args argument list, and makes sure each one is a valid type, then puts it in a Hashtable. Argument name (must be a string), then argument (in your case, this will be an object). The main function will access the arguments as elements of the Hashtable.
It's not type safe at all. But I think it's the only way to do what you're asking for.
Related
I am building a helper function to map properties of an object to an object, which knows to which UI component the property is bound and how to convert their values back and forth.
Now I need the map itself, to map property names (of type String) to their ModelFieldItems, which contain a generic converter (eg StringToBoolConverter). But due to the lack of the <?> Operator, I'm used to from java, I can't simply write private Dictionary<String, ModelFieldItem> items = new Dictionary<String, ModelFieldItem<?,?>>();
where ModelFieldItem contains the type Information and the converter itself.
As a result I need to add specific Types if I want to add a ModelFieldItem to the map, but they differ in type.
I already tried to use dynamic or object as type parameters, but sooner or later I reach a point, where it does not work.
Using the trick from C# - Multiple generic types in one list I got the compiler happy, but I needed a typecast to access my converter logic, which led me to the following condition
if (item is ModelFieldItem<dynamic, dynamic>)
{
ModelFieldItem<dynamic, dynamic> dynamicItem = (ModelFieldItem<dynamic, dynamic>)item;
Which always resolves to false.
EDIT
The Method, which starts the conversion, does not know, which types to convert, which is desired. We are using a loop to iterate over the properties and start their corresponding converters. So casting at the point of conversion can not be done with the distinct types, because we can not know them.
Our converters are as easy as they can be, inheriting from the following abstract class
public abstract class TypedConverter<A, B>
{
public abstract B convertFrom(A value);
public abstract A convertTo(B value);
}
As mentioned before, I'm from a java background, so what I want to achieve looks roughly like the following in Java
private Map<String, ModelFieldItem<?,?>> converters = new HashMap<>();
and I would use it roughly like
converters.put("key", new Converter<String, Boolean>());
How can I achieve this in C#?
Usually when you encounter this type of problem, you have to use an abstraction of your class that doesn't require the generic types.
If your ModelFieldItem implement an interface without generic parameters, you can use it.
var _dict = new Dictionary<String, IModelFieldItem>()
{
new Converter<string, bool>() // If it's implement IModelFieldItem
};
(YouWillHaveToCastUnlessYouUseDynamicType)_dict[key].Convert("true");
Otherwise, another way would be to replace the ModelFieldItem from Dictionary<String, ModelFieldItem> by object or dynamic, then you can cast it when accessing to the value from your dictionary.
var _dict = new Dictionary<String, object>()
{
new Converter<string, bool>()
};
// You can avoid the cast too by using dynamic
// But it will cost you some perf
((Converter<string, bool>)_dict[key]).Convert("true");
if you know the type that you want.
You can do something like that:
var _dict = new Dictionary<String, object>()
{
new Converter<string, bool>()
};
public void Convert<TToConvert, TConverted>(string key, TToConvert valueToConvert, out TConverted valueConverted)
{
valueConverted = (T)_dict[key].Convert(valueToConvert);
}
bool value;
Convert("Key", "true", out value);
Here an other example of what you could do:
public static void Convert<TToConvert, TConverted>(TToConvert valueToConvert, out TConverted valueConverted)
{
// You should put the dictionary outside of the method
// To avoid to instance it, each time you call this method
var dict = new Dictionary<Type, Func<object, object>>()
{
{ typeof(Tuple<string, int>), x => int.Parse((string)x) },
{ typeof(Tuple<string, bool>), x => bool.Parse((string)x) }
};
valueConverted = (TConverted)dict[typeof(Tuple<TToConvert, TConverted>)](valueToConvert);
}
static void Main(string[] args)
{
bool boolTest;
Convert("false", out boolTest);
Console.WriteLine(boolTest);
int intTest;
Convert("42", out intTest);
Console.WriteLine(intTest);
Console.ReadKey();
}
Obvisouly, you should try if you can convert your type first and also if the conversion succeed. Finally make Convert return an boolean to know if it succeed or not.
But at least as you can see, there is no more string key required to make the conversion and it might interests you.
You also have to be sure that your variables have the right type when you pass them to the method, otherwise you will search for the wrong key.
Reflection solution:
With the above method you can do something like that:
static void Main(string[] args)
{
object[] parameters = new object[] { "false", true };
typeof(Program).GetMethod("Convert")
// Be sure that the types will create a valid key
.MakeGenericMethod(new Type[] { parameters[0].GetType(), parameters[1].GetType() })
// Change null to your instance
// if you are not calling a static method
.Invoke(null, parameters);
// parameters[1] is an out parameter
// then you can get its value like that
Console.WriteLine(parameters[1]);
Console.ReadKey();
}
With properties, that should look like this:
object[] parameters = new object[]
{
propertyToRead.GetValue(objectToRead),
propertyToSet.GetValue(objectToSet)
};
typeof(MapperObject).GetMethod("Convert")
.MakeGenericMethod(new Type[]
{
propertyToRead.PropertyType,
propertyToSet.PropertyType
})
.Invoke(mapperInstance, parameters);
propertyToSet.SetValue(objectToSet, parameters[1]);
You may need to adjust it a bit, since I didn't try to compile it
I could give another solution, but I neither know what is your inheritance architecture nor how your Converter works.
I'm not quite sure the correct way of asking this question, so I'm just going to go for it.
I am looking for the cleanest way of passing in "parameter sets" into a Method. For instance:
Dictionary<string, string> Parameters = new Dictionary<string, string>();
Parameters.Add("UserID", "12");
Parameters.Add("SiteID", "43");
CreateLog("Hello World!", Parameters);
public void CreateLog(string Message, Dictionary<string, string> Parameters)
{
...
}
However, I would like to do this in a much cleaner way and I'm sure there is some method of doing it that I can't think of. I could make a custom object that takes a string and an object, for instance:
public void CreateLog(string Message, params LogParameter[] Parameters)
{
...
}
Then I could create a new Log Parameter for each Parameter and pass it in, but I would like to be a bit more concise than that and avoid having to create all those new objects for something that (I think) should be simpler.
One possible solution is to do something like this:
CreateLog("Hello World!", new { UserID = 12, SiteID = 43 });
public void CreateLog(string Message, object Parameters)
{
...
}
This is similar to how C# ASP.NET creates URL variables/how DevExpress creates callbacks for their controls. But how do they get the values out of the object in the method?
Any thoughts?
Three options as I see it:
Use a dictionary as you've already suggested.
Create a class that functions as a DTO and pass that:
public class LogParameters
{
public int UserId { get; set; }
public int SiteId { get; set; }
}
public void CreateLog(string message, LogParameters parameters)
Use an anonymous object, an approach you've also suggested. In your method, you'll have to rely on reflection to transform the object into something you can actually utilize.
Microsoft provides a way to do this via HtmlHelper.AnonymousObjectToHtmlAttributes, but so you don't need a dependency on the System.Web.Mvc namespace, the source for that method is as follows:
public static IDictionary<string, object> AnonymousObjectToHtmlAttributes(object htmlAttributes)
{
Dictionary<string, object> result;
var valuesAsDictionary = htmlAttributes as IDictionary<string, object>;
if (valuesAsDictionary != null)
{
result = new Dictionary<string, object>(valuesAsDictionary, StringComparer.OrdinalIgnoreCase);
}
else
{
result = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
if (htmlAttributes != null)
{
foreach (var prop in htmlAttributes.GetType().GetRuntimeProperties())
{
var value = prop.GetValue(htmlAttributes);
result.Add(prop.Name, value);
}
}
}
return result;
}
That should give you the inspiration you need to adapt it for your own code.
Personally, I generally go with approach 1 or 2 in these scenarios. The syntactic sugar of using an anonymous object is tempting, but if your method depends on the proper params being provided, it can be a crap shoot. Honestly, the dictionary approach suffers in this way as well, but at least there, you're not dealing with reflection.
Technically speaking, methods should be self-documenting, which means you should be passing in either individual named params or a class instance. That way, you have the assurance that what you expect to be available to your method is there, and the end-user of the method can see at a glance what's required to satisfy it. Neither a dictionary nor an anonymous object gives you either of these, and if you notice, the only times Microsoft uses anonymous objects as a param, the contents are completely optional, such as htmlAttributes in a helper. If certain keys don't exist, then it's no big deal, since it's all optional anyways.
Here's the catch, how you can use reflection to get properties out of a Anonymous type passed as object to a method.
public void CreateLog(object parameters)
{
foreach(var property in parameters.GetType().GetProperties())
{
WriteLog(string.Format("{0} - {1}",property.Name, property.GetValue(parameters)));
}
}
Usage:
CreateLog(new {SiteID = 1, UserId = 2});
I am trying to remove duplicate code for collections of System.Func with variable numbers of arguments. (This is used to allow code to "hook" into the logic of other components.)
So I am trying to remove several almost identical methods and use generic methods instead.
To make it work I wrapped them in a HookContainer class and made it implement a common interface.
public interface IHookContainer
{
void Add<TFunc> (string filterName, int priority, KeyValuePair<string, TFunc> action);
}
public class HookContainer<T>:IHookContainer
{
Dictionary<string,OrderedDictionary<string,T>> dict = new Dictionary<string, OrderedDictionary<string, T>> ();
public void Add<T> (string filterName, int priority, KeyValuePair<string, T> action)
{
// Add an item to the Dictionary
}
}
This allows me to put them all in a single Dictionary which I can access with the Type
Dictionary<Type,IHookContainer> containers = new Dictionary<Type, IHookContainer> (){
{
typeof(HookContainer<System.Func<object,object>>),new HookContainer<System.Func<object,object>>()
},
// More dictionary entries like this (with more parameters)
};
public string AddFilter<T> (string filterName, T action, string filterTag = null, int priority=0)
{
KeyValuePair<string, T> data = new KeyValuePair<string, T> (filterTag, action);
if (containers.ContainsKey (typeof(T))) {
IHookContainer container = containers [typeof(T)];
container.Add<T> (filterName, dictPriority, data);
}
return filterTag;
}
Now I get a compile error saying:
cannot convert `System.Collections.Generic.KeyValuePair<string,T>'
expression to type `System.Collections.Generic.KeyValuePair<string,T>'
So it's apparently lacking some information that I take for granted. Or I am thinking wrong. Or both.
Why can't it convert a type to ...itself??
I apparently cannot use the same Type parameter from the Generic class for the generic method if the implemented interface. Sadly, the type cannot be inferred either. Is there any way to make this work?
There is also a warning that the type paramater name is the same as the outer type paramemter. Can I tell the compiler that this is completely intended?
Thanks for any help. I am using C# 4.0
Update:
I can get the code to work by declaring the HookContainer with a second Type parameter and using this for passing it into the Add method.
So it looks like this now:
public class HookContainer<T,U>:IHookContainer
{
Dictionary<string,OrderedDictionary<string,T>> dict = new Dictionary<string, OrderedDictionary<string, T>> ();
public void Add<U> (string filterName, int priority, KeyValuePair<string, T> action)
{
// Add an item to the Dictionary
}
}
This, of course requires me to redundantly pass the Type of the Container twice when declaring the Dictionary:
Dictionary<Type,IHookContainer> containers = new Dictionary<Type, IHookContainer> (){
{
typeof(HookContainer<System.Func<object,object>,System.Func<object,object>>),new HookContainer<System.Func<object,object>,System.Func<object,object>>()
}
};
So while this might work, it looks and feels ridiculous. Is there any way to just (re)use a single Type parameter?
Update
I misread your question. The way you implement this, is not what you want I think. The declaration in your interface is different from what you want in your implementation.
In your interface declaration you specify that implementations of this interface should have a generic function (which can be called with all kinds of types). In your implementation you want to limit this just to the type that you specify in the class.
The class implementation will be for one specific type.
I.e. the IHookContainer interface specifies you can call the Add function with int,string, etc. Whereas in HookContainer<string> you can only call Add with a string argument.
A more suitable implementation to what you want, but you can not store that in the Dictionary is:
public interface IHookContainer<T>
{
Add(string filterName, int priority, KeyValuePair<string, T> action);
}
What a solution can be (what I might do) is to change the Dictionary to a List and create a function that retrieves the correct IHookContainer<T>. Or keep your dictionary when the number of items is large and address the correct one by key.
private List<object> containers = new List<object>();
private IHookContainer<T> GetCorrectHookContainer<T>()
{
// might need to add when not available?
return containers.OfType<IHookContainer<T>>().FirstOrDefault();
}
your AddFilter function will be something like this (pseudo-code)
public void AddFilter<T>(...)
{
GetCorrectHookContainer<T>().Add(...);
}
Old:
Because you specified the type argument (again) in your function, the T of the class is different from the T in the function. The T argument in your generic function 'hides' the T of the class. You can just use it. You do not have to specify this in your function. Your implementation can be:
public void Add(string filterName, int priority, KeyValuePair<string, T> action)
{
// Add an item to the Dictionary
}
example use:
var x = new HookContainer<int>();
x.Add("test", 1, new KeyValuePair<string,int>("test", 4));
It seems that the problem is TFrom from Add and T from HookContainer are two different generic types.
I don't know if it's possible, but I would use the C's main signature (int argc, char[] argv) instead of the Func functions:
System.Func<int, object[], object>
I'm taking my first steps in c # & asp.net and I'm enjoying it much.
Now, I have a question...
Is there a function in C# to call a class/method as in php?
For example:
$class = array(
"foo", // class name
"bar" // method name
);
$params = array(
"one",
"two"
);
call_user_func_array($class, $params); //execute Foo->bar("one","two");
Nope, there is nothing built in to do exactly that. You could build a method that does something similar using reflection, but it seems like a solution looking for a problem.
void Main()
{
CallUserFuncArray("UserQuery+Foo", "Bar", "One", "Two");
}
// Define other methods and classes here
public class Foo
{
public static void Bar(string a, string b){}
}
public void CallUserFuncArray(string className, string methodName, params object[] args)
{
Type.GetType(className).GetMethod(methodName).Invoke(null, args);
}
As others have noted, there are multiple ways to simulate this, but no "baked-in" functionality in C#. The most flexible way is with reflection, but you can do it in a much simpler (and easier to deal with) way if you know the list of methods you'll be calling beforehand.
class Foo
{
public static string FooA(int p1, int p2)
{
return "FooA:" + p1 + p2;
}
public static string FooB(int p1, int p2) { return "FooB:" + p1 + p2; }
public static string FooC(int p1, int p2) { return "FooC:" + p1 + p2; }
}
class Bar
{
//You can use Func<int, int, object> instead of a delegate type,
//but this way is a little easier to read.
public delegate string Del(int p1, int p2);
public static string DoStuff()
{
var classes = new Dictionary<string, Dictionary<string, Del>>();
classes.Add("Foo", new Dictionary<string, Del>());
classes["Foo"].Add("FooA", Foo.FooA);
classes["Foo"].Add("FooB", Foo.FooB);
classes["Foo"].Add("FooC", Foo.FooC);
//...snip...
return classes["Foo"]["FooA"](5, 7);
}
}
Which, by the way, does work.
If you don't know which methods you want to make available this way, I suggest you reconsider whaever you're trying to do. The only reason I can think of for using strings to choose your execution path would be if you were planning to get those strings from the user. Not only is it a huge no-no to expose inner details of your application like this, but it comes dangerously close to eval-type functionality. There's a reason C# doesn't have an eval method, and it isn't because the designers forgot to put it in.
As #StriplingWarrior said, there's no built-in equivalent of call_user_func_array, but you can do something like it with Reflection.
The problem is that Reflection code can get very complicated very quickly, and can be brittle and error-prone if you're not VERY careful.
For example the following function does what you want:
public static void CallUserFuncArray(string[] func, params string[] args)
{
var type = Type.GetType(func[0]);
if (type == null)
{
throw new ArgumentException("The specified Class could not be found");
}
var method = type.GetMethod(func[1], BindingFlags.Static | BindingFlags.Public);
if (method== null)
{
throw new ArgumentException("The specified Method could not be found");
}
method.Invoke(null, args);
}
You call it like this:
var func = new [] { "Foo", "Bar" };
var args = new [] { "one", "two" };
CallUserFuncArray(func, args);
The problems though are many.
The code only works if Bar is a public static method.
There's a whole new layer of complexity if you need to call an instance method on an object.
The code will explode if the parameters in the args array aren't just right for the target method.
There's no support here for calling methods that expect anything other than string parameters. It's possible to query the type of the arguments expected by the method and convert the types before calling 'Invoke', but you're getting even more messy.
There are many more edge cases that blow out the complexity of this code even more if you need to cater for them.
To paraphrase Carl Franklin (of dotNetRocks fame):
I had a problem I needed to solve, so I used Reflection. Now I have two problems.
I you find yourself need to do this sort of thing thenyou probably need to rethink your overall design.
How can I pass anonymous types as parameters to other functions? Consider this example:
var query = from employee in employees select new { Name = employee.Name, Id = employee.Id };
LogEmployees(query);
The variable query here doesn't have strong type. How should I define my LogEmployees function to accept it?
public void LogEmployees (? list)
{
foreach (? item in list)
{
}
}
In other words, what should I use instead of ? marks.
I think you should make a class for this anonymous type. That'd be the most sensible thing to do in my opinion. But if you really don't want to, you could use dynamics:
public void LogEmployees (IEnumerable<dynamic> list)
{
foreach (dynamic item in list)
{
string name = item.Name;
int id = item.Id;
}
}
Note that this is not strongly typed, so if, for example, Name changes to EmployeeName, you won't know there's a problem until runtime.
You can do it like this:
public void LogEmployees<T>(List<T> list) // Or IEnumerable<T> list
{
foreach (T item in list)
{
}
}
... but you won't get to do much with each item. You could call ToString, but you won't be able to use (say) Name and Id directly.
Unfortunately, what you're trying to do is impossible. Under the hood, the query variable is typed to be an IEnumerable of an anonymous type. Anonymous type names cannot be represented in user code hence there is no way to make them an input parameter to a function.
Your best bet is to create a type and use that as the return from the query and then pass it into the function. For example,
struct Data {
public string ColumnName;
}
var query = (from name in some.Table
select new Data { ColumnName = name });
MethodOp(query);
...
MethodOp(IEnumerable<Data> enumerable);
In this case though, you are only selecting a single field, so it may be easier to just select the field directly. This will cause the query to be typed as an IEnumerable of the field type. In this case, column name.
var query = (from name in some.Table select name); // IEnumerable<string>
You can't pass an anonymous type to a non generic function, unless the parameter type is object.
public void LogEmployees (object obj)
{
var list = obj as IEnumerable();
if (list == null)
return;
foreach (var item in list)
{
}
}
Anonymous types are intended for short term usage within a method.
From MSDN - Anonymous Types:
You cannot declare a field, a property, an event, or the return type of a method as having an anonymous type. Similarly, you cannot declare a formal parameter of a method, property, constructor, or indexer as having an anonymous type. To pass an anonymous type, or a collection that contains anonymous types, as an argument to a method, you can declare the parameter as type object. However, doing this defeats the purpose of strong typing.
(emphasis mine)
Update
You can use generics to achieve what you want:
public void LogEmployees<T>(IEnumerable<T> list)
{
foreach (T item in list)
{
}
}
"dynamic" can also be used for this purpose.
var anonymousType = new { Id = 1, Name = "A" };
var anonymousTypes = new[] { new { Id = 1, Name = "A" }, new { Id = 2, Name = "B" };
private void DisplayAnonymousType(dynamic anonymousType)
{
}
private void DisplayAnonymousTypes(IEnumerable<dynamic> anonymousTypes)
{
foreach (var info in anonymousTypes)
{
}
}
Normally, you do this with generics, for example:
MapEntToObj<T>(IQueryable<T> query) {...}
The compiler should then infer the T when you call MapEntToObj(query). Not quite sure what you want to do inside the method, so I can't tell whether this is useful... the problem is that inside MapEntToObj you still can't name the T - you can either:
call other generic methods with T
use reflection on T to do things
but other than that, it is quite hard to manipulate anonymous types - not least because they are immutable ;-p
Another trick (when extracting data) is to also pass a selector - i.e. something like:
Foo<TSource, TValue>(IEnumerable<TSource> source,
Func<TSource,string> name) {
foreach(TSource item in source) Console.WriteLine(name(item));
}
...
Foo(query, x=>x.Title);
You can use generics with the following trick (casting to anonymous type):
public void LogEmployees<T>(IEnumerable<T> list)
{
foreach (T item in list)
{
var typedItem = Cast(item, new { Name = "", Id = 0 });
// now you can use typedItem.Name, etc.
}
}
static T Cast<T>(object obj, T type)
{
return (T)obj;
}
Instead of passing an anonymous type, pass a List of a dynamic type:
var dynamicResult = anonymousQueryResult.ToList<dynamic>();
Method signature: DoSomething(List<dynamic> _dynamicResult)
Call method: DoSomething(dynamicResult);
done.
Thanks to Petar Ivanov!
To pass anonymous types around, consider using dynamic. A longer example is shown below and the technique you can use. For example, consider calling the TreadSafeDynamicObject here 'CustomEmployee' to make more sense of the code. The code has a constructor that accepts a dynamic object (your anonymous, potentially nested class), for example :
var someCustomEmploye = new {
IsIntern = false,
EmployeeFacts = new {
IsSenior = true,
BirthDate = new DateTime(1960, 1, 1)
}
};
You can transform someCustomEmployee to a dynamic object using the technique shown below, for example pass in 'someCustomEmployee' into the constructor, in my code it would be:
dynamic someEmp = new ThreadSafeDynamicObject(someCustomEmployee);
Once you have transformed someEmp into a proper dynamic object, your LogEmployee function can for example serialize the object and log it or handle it in some other way (note that you do not have to go via converting it to a dynamic object anyways, if it is enough to just serialize the anonymous class instance).
Example :
dynamic threadSafeToyota = new ThreadSafeDynamicObject(new {
Make = "Toyota",
Model = "CR-H",
Propulsion = new {
IsHybrid = true,
UsesPetrol = true,
ElectricMotor = true
}
});
The code accepts a dynamic object and uses a private method 'ToDictionary' to convert the object graph of the anonymous class instance provided as an alternative way to set properties on the dynamic object.
Note that I also have added some more code here to provide thread safety when setting and getting properties.
public class ThreadSafeDynamicObject : DynamicObject, IEnumerable<KeyValuePair<string, object>>
{
public ThreadSafeDynamicObject()
{
}
public ThreadSafeDynamicObject(dynamic members)
{
var membersDict = ToDictionary(members);
InitMembers(membersDict);
}
private IDictionary<string, object> ToDictionary(object data)
{
var attr = BindingFlags.Public | BindingFlags.Instance;
var dict = new Dictionary<string, object>();
foreach (var property in data.GetType().GetProperties(attr))
{
if (property.CanRead)
{
dict.Add(property.Name, property.GetValue(data, null));
}
}
return dict;
}
private void InitMembers(IDictionary<string, object> membersDict)
{
foreach (KeyValuePair<string, object> member in membersDict){
_members.AddOrUpdate(member.Key, member.Value, (key, oldValue) => member.Value);
}
}
private readonly ConcurrentDictionary<string, object> _members = new();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _members.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_members.AddOrUpdate(binder.Name, value, (key, oldvalue) => value);
return true;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return _members.Keys.ToList().AsReadOnly();
}
public override string ToString()
{
return JsonSerializer.Serialize(_members);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return _members.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _members.GetEnumerator();
}
}
When running the code inside Linqpad 7 I got this output (I am using static System.Console and using System.Dynamic here):
WriteLine(threadSafe.ToString());
WriteLine(threadSafe.Make);
WriteLine(threadSafe.Model);
WriteLine(threadSafe.Propulsion.IsHybrid);
WriteLine(threadSafe.Propulsion.UsesPetrol);
WriteLine(threadSafe.Propulsion.ElectricMotor);
There are several advantages to this. It supports nested levels as you can see in the output and is very flexible. The method 'ToDictionary' is essential here. Also, we do not have to use additional libraries outside the .net framework, so the funtionality is built in. I have not checked all variants of this code, at least it supports the typical scenarios of anonymous type object graphs.
Key thing here is to transform your anonymous type first into a dictionary and then populate the internal concurrent dictionary with the members ('fields' or 'props') of the derived DynamicObject.
There are several ways to solve this :
You could do boxing. E.g. have a method that accepts object and use reflection to extract the properties and log the properties and their values
E.g. :
public void LogEmployees(object someCustomEmployee) { // .. }
You could transform the anonymous object into a dynamic object as shown in my sample
In addition to boxing or converting into a dynamic object, you could avoid reflection by serializing the converted object (either boxed object or dynamic variant).
If you know, that your results implements a certain interface you could use the interface as datatype:
public void LogEmployees<T>(IEnumerable<T> list)
{
foreach (T item in list)
{
}
}
I would use IEnumerable<object> as type for the argument. However not a great gain for the unavoidable explicit cast.
Cheers