I have a value I'm getting dynamically. This is because it exists in an abstraction method I'm using to build a query from a dot-notation representation of deeply related property.
I need to know if the value contains a particular value. This is easy enough for a string, but when the object in question is a collection I can't figure how to check. I can assume it's a List in my situation, but I don't know what type of list, so I can't achieve a property casting with something like List<int>.
So where x is an object and spec is dynamic this is how I test for string:
return x.ToString()?.Contains(spec, StringComparison.InvariantCultureIgnoreCase) ?? false;
This successfully determines if it's a collection type, but I still can't call Contains on it:
if (x.GetType().Namespace == "System.Collections.Generic")
{
return x.Contains(spec); //does not work
}
Also tried:
var list = (List<dynamic>)x;
var list = (List<object>)x;
But this fails in my test scenario with List<int>
Related
So currently, you can imagine I have 1 method that is the constructor that funcitons like
info.PersonalInfo=getPersonalInfo(Id);
info.MedicalInfo=getMedicalInfo(Id);
Thing is, all of those get data and get binarys are repeating 95% of the code
using (CVDataEntities data = new CVDataEntities())
{
var temp = data.PersonalInfo.Where(m => m.Id == Id).FirstOrDefault();
return temp;
}
The only thing that changes is instead of PersonalInfo its MedicalInfo.
I thought of using a switch and just sending a number as the selector for which specific object I would want.
But the problem is the method is made so that it can only return
public IEnumerable<PersonalInfo> getPersonalInfo (string Id)
Is there any way for me to make a IEnumerable that lets me return any object, or is there a better way to go about this. I want to do it mostly to reduce the code from 400 lines down to 200 at most.
Try using generic methods, you will be able to specify the return type of your function when you call it. This could make your code look like this :
public IEnumerable<T> getInfo<T>(string id)
{
// Some code
}
// Calling the function
info.PersonalInfo = getInfo<PersonalInfo>(Id);
info.MedicalInfo = getInfo<MedicalInfo>(Id);
But be careful while using it, cause the compiler won't know what type T is (it is only defined at runtime) so it could lead to some errors while processing the data (like missing properties / methods exclusive to a specific type)
EDIT : Johnathan Barclay made a good point by commenting that the // some code bit is relevant and asked "How would the correct property be selected on data? How do you access an Id property on T?"
To get the correct property and access an Id property, you could use System.Reflection and add a string parameter to get the name of the property you want to use, and another to give the Id property name to the function:
public IEnumerable<T> getInfo<T>(string id, string propertyToReadName, string propertyToCompareName)
{
using (CVDataEntities data = new CVDataEntities())
{
// Getting the enumerable not filtered first
IEnumerable<T> unfilteredList = (IEnumerable<T>)data.GetType() // Get the type
.GetProperty(propertyToReadName, typeof(T)) // Get the property (PersonalInfo or MedicalInfo)
.GetValue(data); // Get the value of this property in the `data` instance
// Filtering the list
IEnumerable<T> filteredList = unfilteredList.Where(m =>
typeof(T).GetProperty(propertyToCompareName) // Get the "id" property using parameter
.GetValue(m) // Get the "id" value of m instance
.Equals(id)); // Check if it equals the id given as parameter
return filteredList;
}
}
// Calling the function
info.PersonalInfo = getInfo<PersonalInfo>(Id, "PersonalInfo", "Id");
info.MedicalInfo = getInfo<MedicalInfo>(Id, "MedicalInfo", "Id");
If you want to return a single element instead of an IEnumerable don't forget to change the return type of the function from IEnumerable<T> to T and add .FirstOrDefault() at the return line
Note that you could also give another value to the parameter propertyToCompareName and make a comparison to another property of the T class
I have method which accepts an object. This object I know is a List<T> however T may vary between children of a base class at any one time when being passed into the method.
So if my base class is MonthType, and I have children called BlockMonthType and AreaMonthType the object passed in could be anyone of List<BlockMonthType> or List<AreaMonthType>.
I want to be able to add items to this object however when I cast it it seems to make a copy and the original object is not updated.
I'm doing this to cast:
var objectList = ((IEnumerable<MonthType>)graphObject.Source.Object).ToList();
Now I want to create a new item and add it to the list
// where ObjectType is a Type variable containing BlockMonthType
var newObject = (BlockMonthType)Activator.CreateInstance(graphObject.Source.ObjectType);
objectList.Add(newObject);
// and carry on the world is good
This works in so far as objectList has a newObject added. However the original variable isn't updated so when I leave the method it's back to it's original state. I know the object is a List<> when passed in as I can see it in the debugger as such.
Is there anyway I can accomplish this?
Here is a cut down version of the method I'm using it in.
public TraverseGraphResult Write(ObjectGraph graphObject)
{
var objectList = ((IEnumerable<MonthType>)graphObject.Source.Object).ToList();
var newObject = (MonthType)Activator.CreateInstance(rule.ObjectType);
newObject.Month = rule.Month;
objectList.Add(newObject);
// Other stuff as well is done but that's the crux of it
}
Hopefully this gives it more context. The method is being used to try and navigate a large object tree with many class types. I'm trying to add a new class type handler which will deal with adding and removing items from a list.
// This is being used in a recursive method to loop down a object's property tree
// .. more code here
// where properties is a List<PropertyInfo>
foreach (var pInfo in properties)
{
if (IsList(pInfo.PropertyType))
{
var enumerable = (IEnumerable)pInfo.GetValue(currentObjectGraph.Source.Object, null);
var sourceEnumerator = enumerable.GetEnumerator();
var graph = new ObjectGraph(enumerable, pInfo.Name);
// this part is made up but essentially the code looks up a list of objects that can deal with this
// particular one and returns it. We then call the write method on that object
var something = GetInterfaceHandlerForObject(enumerable);
something.Write(graph);
}
}
You should make your method generic:
public void MyMethod<T>(List<T> objectList) where T:class, new()
{
objectList.Add(new T());
...
}
Casting is rarely ever necessary when you use generics. Also, your ToList() is causing a new copy of the list to be created.
One drawback to this approach is that T needs to have an empty constructor. If you need to construct an object with parameters you could instead pass in a Func<T>. You can then call it passing in a lambda expression like: (x) => new BlockMonthType(someParameter, orAnother).
I ended up resolving this by storing the underlying List T type in the ObjectGraph object and casting to that when required.
var objectList = ((IEnumerable)graphObject.Source.Object).Cast(monthAllocationRule.ListType);
Without the correct cast objectList was either null or a copy of the list. Now I can add to objectList and know it's added to the source object.
Probably not idea as Ian mentioned above but did the trick.
Given a function
T Func<T>(string val) {}
where the goal is to convert val to T, where T could be int, or could also be List (in which case val is a comma delimited list of things that hopefully can be converted to Ts
ignoring the int version, because it is easy, how do you convert the string to a List
As I was entering this question, the answers came to me, so I leave this question answered as a guide to others.
I have a function such as
T Func<T>(string val) {}
Inside this function I do various processing, one item of which is to convert the val to a T.
I have another function which I use for this purpose
T ChangeType<T>(this object value)
This function is mainly a wrapper around Convert.ChangeType with some additional processing, to handle things that Convert.ChangeType can't do out of the box (stolen from stackoverflow elsewhere)
Everything works great for most types, but is some cases T is a List and val is then a comma separated list of values.
I can correctly determine if T is a List via
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(List<>))
and then I can instantiate the appropriate list return type as follows
T list = (T)Activator.CreateInstance(returnType);
Now, I need to call my ChangeType on each split value from the string, and add them to the list.
var method = typeof (XmlUtil).GetMethod("ChangeType", new [] {typeof(object)});
var genericMethod = method.MakeGenericMethod(subType);
var convertedItem = genericMethod.Invoke(null, new [] {item});
var subType = returnType.GetGenericArguments()[0];
All good so far. Now I have a
dynamic list; //instantiated with a List<TT>
var convertedItem; // which is typed as an object, instantiated with a type TT.
How do I call
list.Add(convertedItem);
the dynamic will handle the fact that I don't have a known interface for the list, but at runtime I get an error because I am trying to add an object to a list, even though the object is actually of type T as instantiated.
The ultimate answer, is to cast the List as a non-generic IList, where you can then add object.
System.Collections.IList list2;
list2 = (System.Collections.IList) list;
list2.Add(convertedItem);
I have dictionary that gives me back a method according to the value passed in. Defined as so:
Dictionary<Type, IXmlWriterConverter>
I have now added a new function that which has the Key/type set to IEnumerable, so far so good.
But when I execute my unit test with a List containing two DataTables but the dictionary can not find the key e.g. my type conversion differs.
Why is that so? And what would be the right attempt to solve my problem?
Edit: Sorry here is the requested code ;-)
Function that generates the testvalues:
public IEnumerable<DataTable> CreateTestDataTableList()
{
var resultDataTable = new List<DataTable>();
resultDataTable.Add(CreateTestTable("testTable1", 2));
resultDataTable.Add(CreateTestTable("testTable2", 3));
return resultDataTable;
}
Function called by the unit test:
public void Write(XmlWriter xmlWriter, object value)
{
...
converter = FindConverter(value.GetType());
}
Function checking the dictionary:
public IXmlWriterConverter FindConverter(Type type)
{
if(Converters.ContainsKey(type))
{
return Converters[type];
}
return null;
}
2.Edit:
Code that adds the values to the Dictionary:
public void Add(IXmlWriterConverter xmlWriterConverter)
{
if(Converters.ContainsKey(xmlWriterConverter.InputType))
{
Remove(xmlWriterConverter);
}
Converters.Add(xmlWriterConverter.InputType, xmlWriterConverter);
}
The InputType is a readonly (get) property of the converter. I checked the type added to the dictionary and that was registered as IEnumerable, however when I checked on the typeof when passing in my list the type was List and not IEnumerable. I was told that this happens because I pass in the values as object.
This is a really code-stinky solution to me, and it cuts down on the efficiency, but you can also iterate through the GetInterfaces() method on Type, like this:
List<DataTable> l = new List<DataTable>();
var t = l.GetType();
var ints = t.GetInterfaces();
Then you could do a lookup on the type, and, if that doesn't work do a lookup on it's interfaces.
However, this feels like a terrible hack, which usually indicates that some more design work needs to be done. Is it not possible to put the List type in the dictionary? Is there no better way of doing this lookup?
Also, a note on doing dictionary lookups: It's more efficient to use the TryGetValue method, like this:
public IXmlWriterConverter FindConverter(Type type)
{
IXmlWriterConverter converter;
if( Converters.TryGetValue(type, out converter) )
{
return converter;
}
return null;
}
When you do it this way, it only does one lookup on the dictionary, whereas if you use ContainsKey it has to do two lookups.
Its a bit of hack but the only thing that gets into my mind when looking at your code, is to add an other generic write method:
public void Write<TValue>(XmlWriter writer, TValue value) {
// ...
}
This allows to identify the right type for IEnumerable and leaves the other Write method to not break any existing code.
Probably you are trying to retrieve List type and not IEnumerable (inheritance is not going to work in this context)
Please paste the code that does the lookup if you want more details and more certain answer :)
You should also post the code of where you are retrieving the test values. But based on what you've given, are you possibly using the non-generic IEnumerable to retrieve the values instead of the generic version that you're using to generate them?
The InputType is a readonly (get)
property of the converter. I checked
the type added to the dictionary and
that was registered as IEnumerable,
however when I checked on the typeof
when passing in my list the type was
List and not IEnumerable. I was told
that this happens because I pass in
the values as object.
Like they say on MythBusters, well, there's your problem! Even though List is-a IEnumerable the Type objects that represent each are definitely not the same. In programmer jargon, typeof(List) != typeof(IEnumerable). That is why the lookup is not working.
The short story: I want to convert a list/dictionary into an anonymous object
Basically what I had before was:
var model = from item in IEnumerable<Item>
select new
{
name = item.Name
value = item.Value
}
etc.
If I have name, item.Name in a list or dictionary, how can I go about creating the same anonymous object model?
Edit: Clarification:
If the dictionary contains [name, item.Name] and [value, item.Value] as Key Value pairs, how would I go about creating the model without assuming that you know neither name nor value?
Since a List<T> implements IEnumerable<T> your existing code should work exactly the same way:
var model = from item in yourList
select new { name = item.Name };
For a Dictionary<TKey,TValue> you could simply do this:
var model = from item in yourDictionary
select new {
name = item.Key
value = item.Value
};
This works because Dictionary<TKey,TValue> implements IEnumerable<KeyValuePair<TKey,TValue>> so in the second expression item will be typed as KeyValuePair<TKey,TValue> which means you can project a new type using item.Key and item.Value.
It sounds like you just want to get the values from a dictionary? If so, MyDictionary.Values should do the trick for you.
If you want to construct, somewhere else, another anonymous object, that is type-compatible with the one you generate from your IEnumerable<Item>, you can do that by ensuring that the anonymous type you construct has:
The same number of members
Members with the same type
Members with the same name
... in the same order
If you do that, they will map to the same single generated type.
Now, why you would want to do that is beyond me, so I'm pretty sure I didn't understand your question. You should post more information about what you want to do.
Anyway, here's the code to produce an anonymous object that is type-compatible with the one from your IEnumerable:
var x = new
{
name = dict["name"],
value = dict["value"]
};
Since this obeys all the rules above, it will be of the same type as your objects generated from your Linq query.