Pass in "Parameter Sets" in C# - c#

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});

Related

Not sure how to call method with a collection return type with variables

I'm not sure what collection type to use, but I was thinking that a Dictionary would be best for this. I need to be able to create Key Value Pairs and I'd like to be able to iterate over it, preferably with a foreach loop. The tricky part is that I'd like to be able to pass in the key and value (both strings) when I call the method and store them into the collection to use later.
I was thinking something like this:
public static void Main(string[] args)
{
DictionaryTest("this","that");
}
public static void DictionaryTest(Dictionary<string, string> testDictionary)
{
testDictionary.Add(string, string);
foreach (KeyValuePair<string, string> fields in testDictionary)
{
Console.WriteLine(fields.Key);
Console.WriteLine(fields.Value);
}
}
The trouble is, I'm not sure how to set the key value strings up as variables to use. Also, I'm not even sure that using a Dictionary is the way to go here, but I do know that I won't have a fixed number of entries in the collection and that I want to pass all of the key, value pairs to another method later on which are basically used as search fields and search values.
In general, I'd like some advice and possible direction to help guide me here. More of a question about methodology as much as it is about syntax.
Thanks
Update: I ended up doing it this way...
public static void Main(string[] args)
{
Dictionary<string, string> testDictionary = new Dictionary<string, string>();
DictionaryTest("this","that");
}
public static Dictionary<string,string> DictionaryTest(string Key, string Value)
{
Dictionary<string, string> testDictionary = new Dictionary<string, string>();
testDictionary.Add(Key,Value);
foreach (KeyValuePair<string, string> fields in testDictionary)
{
Console.WriteLine(fields.Key);
Console.WriteLine(fields.Value);
}
return testDictionary;
}
I've adjusted your code to what I think you're asking:
//you ever need a class wide variable to hold your dictionary, or you need to make a new one in the Main method
static Dictionary<string, string> myDict = new Dictionary<string, string>();
public static void Main(string[] args)
{
DictionaryTest(myDict, "this","that"); //you didn't pass a dictionary object when you called this method
}
public static void DictionaryTest(Dictionary<string, string> testDictionary, string keyToAdd, string valToAdd) //you didn't have parameters for the strings you passed when you called this method
{
testDictionary[keyToAdd] = valToAdd; //same as Add(), but doesn't crash on dupe
//I generally iterate dictionaries by iterating the keys collection and accessing the dictionary whenever I need to, rather than accessing a collection of keyvaluepair
foreach (string key in testDictionary.Keys)
{
Console.WriteLine(key);
Console.WriteLine(testDictionary[key]);
}
}
It's not the only way to do things.. Many times in coding the way it is done is preference rather than rule, and it is influenced by how the client wants things to work. Don't take this code as a hard and fast rule of how you must do things, it's just one way that works
Adriani6 made a good point about your confusion in the original code:
Dictionary<string, string> simply means "a dictionary whose key is a string and whose value is a string". It's an entire type of object on its own, different to (for example) Dictionary<int, string> - these things are no more the same type than Customer and Order are in the classic sales system. Generics are one of those things where the compiler writes a bunch of code for you before it compiles, meaning the generic Dictionary becomes one dedicated for just string,string (and another totally separate one as int,string for example) - the compiler actually writes a lot of code for us, to make various features of the language work and give us a simpler cleaner syntax to work with than the early days where all this stuff had to be literally written by us humans.
When you stated that your DictionaryTest method took a single parameter of type Dictionary, then tried to pass it two strings, C# won't think "oh, the method asks for a double stringed dictionary, and two strings are being passed, obviously I'll use them as the key and the value.." because it can't for several reasons:
where does it get the actual dictionary object from, to use these strings with?
which one is the key and which is the value?
method resolution purely looks at the type and order of arguments passed, it doesn't look at the types that are stored inside objects and guess. Here's another example:
class Person{
string Name;
int Age;
bool IsMale;
}
...
public void RegisterPerson(Person p) //a method
...
RegisterPerson("John", 31, true);
The RegisterPerson method takes a Person object, c# cannot accept you passing a bunch of primitive types that effectively make up a person, and auto make a person for you and pass it to the method.. you can do it explicitly with
RegisterPerson(new Person(){ Name = "John", Age = 31, IsMale = true });
Or you can define an overload that takes primitives and makes a new person:
public void RegisterPerson(Person p){ ... }
public void RegisterPerson(string name, int age, bool IsMale) {
RegisterPerson(new Person(){ Name = name, Age = age, IsMale = IsMale});
It's just that c# won't do this for you. Hopefully you can see how this also applies to your dictionary situation
Taken from the comments it seems that your issue is that you don't understand the type that the DictionaryTest method expects. It expects a Dictionary, what you get confused about is the <string, string> part. That indicated that the key as well as the value is expected as a string, not any other format.
For a cleaner example:
Dictionary<key type, value type> variableName.
So to work this out, what you need to do is create a dictionary to pass in as an argument.
public static void Main(string[] args)
{
DictionaryTest(new Dictionary<string, string>(){{"this","that"}});
}
Note, I created a Dictionary object with a single value inside of it, key: this and value: that.

How to use a map of generic converters in c#

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.

Nested generic types: Reuse outer Type parameter

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>

Constructor Argument Enums and Magic Strings

I am working on my c# .net application and use implement IoC/DI pattern using Ninject, Now Ninject has a class named ConstructorArgument which takes two arguments(argName,argValue).
So i need to pass static argName something like this
new ConstructorArgument("strVar","")
passing hardcoded string does not seems like a good option.
So i want to create something like dynamics enums using reflection for the constructor arguments, so i dont need to pass hardcoded strings.
Please guide me through this process or suggest me something else to achieve this.
like dynamics enums
There is no such construct readily available. If you really hate the strings, you could write some kind of expression-tree lambda (i.e. () => new Foo(strVal: "") or () => new Foo(""), however - that a: is a lot of work, and b: won't work well if there are other parameters being provided by the container.
To be honest, this is a bit of a non-issue, especially since named arguments mean that parameter names should be treated as a contract. IMO, just use the string. If it worries you, make sure you cover the scenario in a unit test, so that you find out early if it changes.
I agree with #Mark Gravell's stance, except that obfuscators can rename parameters for non-public ctors so the advice doesn't apply in that specific case, so in some cases, you need to whack on a [Obfuscation] on the parameter to preserve the name in some instances.
But I have built nonsense like this which would answer your question. Please don't use it as I regret writing it!
static class StaticReflection<TClass>
{
static string PublicConstructorParameterName<TParameter>()
{
return typeof( TClass ).GetConstructors( BindingFlags.Public | BindingFlags.Instance ).Single().GetParameters().Where( param => param.ParameterType == typeof( TParameter ) ).Single().Name;
}
internal static ConstructorArgument CreateConstructorArgument<TParameter>( TParameter value )
{
return new ConstructorArgument( PublicConstructorParameterName<TParameter>(), value );
}
internal static ConstructorArgument CreateConstructorArgument<TParameter>( Func<IContext, TParameter> argumentResolver )
{
return new ConstructorArgument( PublicConstructorParameterName<TParameter>(), context => (object)argumentResolver( context ) );
}
}
Which works like this:
public class StaticReflectionFacts
{
public class X2
{
}
public class X
{
public X( object param1, X2 param2 )
{
}
}
[Fact]
static void DeriveNinjectConstructorArgumentFromPublic()
{
var newArg = StaticReflection<X>.CreateConstructorArgument( new X2() );
Assert.Equal( "param2", newArg.Name );
}
}
I have imlemented this:
public string GiveConstuctorArgumentName(Type class, Type constructorArgument)
{
var cons = class.GetConstructors();
foreach (var constructorInfo in cons)
{
foreach (var consParameter in constructorInfo.GetParameters())
{
if (consParameter.ParameterType == constructorArgument)
{
return consParameter.Name;
}
}
}
throw new InstanceNotFoundException();
}
Its without LINQ, but its a good start point to understand how its work.

How to pass anonymous types as parameters?

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

Categories