I'm trying to get the value of a specified index of a property using reflection.
This answer works for standard properties that are of type List<> for example, but in my case, the collection I am trying to work with is of a different format:
public class NumberCollection : List<int>
{
public NumberCollection()
{
nums = new List<int>();
nums.Add(10);
}
public new int this[int i]
{
get { return (int) nums[i]; }
}
private List<int> nums;
}
public class TestClass
{
public NumberCollection Values { get; private set; }
public TestClass()
{
Values = new NumberCollection();
Values.Add(23);
}
}
class Program
{
static void Main(string[] args)
{
TestClass tc = new TestClass();
PropertyInfo pi1 = tc.GetType().GetProperty("Values");
Object collection = pi1.GetValue(tc, null);
// note that there's no checking here that the object really
// is a collection and thus really has the attribute
String indexerName = ((DefaultMemberAttribute)collection.GetType()
.GetCustomAttributes(typeof(DefaultMemberAttribute),
true)[0]).MemberName;
// Code will ERROR on the next line...
PropertyInfo pi2 = collection.GetType().GetProperty(indexerName);
Object value = pi2.GetValue(collection, new Object[] { 0 });
Console.Out.WriteLine("tc.Values[0]: " + value);
Console.In.ReadLine();
}
}
This code gives an AmbiguousMatchException ("Ambiguous match found."). I know my collection class is somewhat contrived, but can anyone help with this?
One option is to use
var prop = Type.GetProperties()
.Where(prop => prop.DeclaringType == collection.GetType())
.First();
Change Collection.GetType() to another type if you want. But basically: loop over the properties instead of using Type.GetProperty.
If you are looking for all of the default members, you can ask for Type.GetDefaultMembers(), then examine the members to find the one that you are looking for.
Alternatively, if you know the data type of the indexer, you can call GetPropertyInfo with the type array specifier.
Related
I have a generic method that I want to pass a variable to, and if that variable is IEnumerable, set all its elements to its default value. This is what I have so far:
public static T set_to_default<T>(T the_obj)
{
var the_enumerable = the_obj as IEnumerable;
if (the_enumerable != null)
{
foreach (var element in the_enumerable)
{
// I don't know what to put here
// I want to set each element to its default value: default(T)
}
return the_obj;
}
else
{
return default(T);
}
}
What do I put inside the foreach loop?
You should just work with overloads, it's much simpler (if I understand your question correctly).
public static T SetToDefault<T>(T the_obj)
{
return default(T);
}
public static IEnumerable<T> SetToDefault<T>(IEnumerable<T> the_enumerable)
{
return the_enumerable.Select(value => default(T));
}
FYI I tested my code with this function:
public static void Test()
{
int myInt = 7;
IEnumerable<int> myEnumberable = new List<int>() { 1, 4, 8, 9 };
myInt = SetToDefault(myInt);
myEnumberable = SetToDefault(myEnumberable);
Console.WriteLine($"MyInt: {myInt}");
Console.WriteLine($"MyEnumberable: {String.Join(", ", myEnumberable)}");
}
To add to this, keep in mind that the name SetToDefault isn't a great choice. When you pass in an int, you will get back an int. You still have to set the value yourself (myInt = SetToDefault(myInt);) which is kind of contradictory to what the name of the function implies.
By the way, note that the first function (T SetToDefault<T>(T the_obj)) has a parameter which is never used. To work around this (to be fair, small) issue, you could use an extension method:
public static class Extensions {
public static T GetDefault<T>(this T value) {
return default(T);
}
}
Note that even here, you will have to set the value to the return of the function. Returning void and simply doing value = default(T); will not work for primitive types like int. That's also why I named it GetDefault instead of SetToDefault this time.
var myWhatever = 3.4;
myWhatever = myWhatever.GetDefault();
Inside the for loop put the following statement:
var default_val = Activator.CreateInstance(element.GetType());
This is how you set to default value
and here is a complete working example of your program
class Program
{
static void Main(string[] args)
{
var my_list = new List<int>() { 1, 2, 3 };
var list2 = set_to_default<List<int>>(my_list);
foreach (var elem in list2)
{
Console.WriteLine(elem);
}
Console.ReadKey();
}
public static T set_to_default<T>(T the_obj)
{
IEnumerable the_enumerable = the_obj as IEnumerable;
if (the_enumerable != null)
{
Type type = the_enumerable.GetType().GetGenericArguments()[0];
Type listType = typeof(List<>).MakeGenericType(new[] { type });
IList list = (IList)Activator.CreateInstance(listType);
var looper = the_enumerable.GetEnumerator();
while (looper.MoveNext())
{
var current_obj = looper.Current;
current_obj = Activator.CreateInstance(current_obj.GetType());
list.Add(current_obj);
}
return (T)list;
}
else
{
return default(T);
}
}
}
I suggest to use lists instead of enumerables because the last ones are immutable. That is once you create them you cannot edit them.
Final thing....if you want to support other types of IEnumerable the are not IList you may use the C# 'is' keyword to figure it out. In this case you need to know the underlying type of your enumerable at run-time. You may do that using the C# 'is' keyword
if (the_enumerable is IList)
{
editableType = typeof(List<>).MakeGenericType(new[] { type });
}
else if(the_enumerable is ICollection)
{
......
}
I am at a complete loss here, despite looking at multiple SO posts and anything else I can think of.
My goal here is to make a really, really simple mapper. Something I can basically use as a tool in some unit tests. It doesn't need to be sophisticated or anything -- just map high-level primitive and string values of one object to another. So the basic algorithm is:
Get all properties from TFrom
Get all properties from TTo
Get all properties that are in both, matched by name.
I know this could be a bug in that they could have the same name but a different type, but let's set that aside. It's not what I'm running into here -- the properties and types match between classes.
Create an instance of TTo that we can copy to.
For each property that was mapped between the objects:
Get the value off of the from object
Convert the value to the type of the property
Set the value on the to object
The problem is that no matter what I do, and no matter what the type of the property is (int or string, for example) I get the following:
Object does not match the target type.
Here is the code I'm using:
public TTo Map<TFrom, TTo>(TFrom from)
{
if (from == null) return default;
var fromProps = GetProperties(typeof(TFrom));
var toProps = GetProperties(typeof(TTo));
// Props that can be mapped from one to the other
var propsToCopy = fromProps.Intersect(toProps, new PropertyComparer()).ToList();
var returnObject = (TTo)Activator.CreateInstance(typeof(TTo));
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(returnObject, convertedValue, null);
}
return returnObject;
}
public PropertyInfo[] GetProperties(Type objectType)
{
var allProps = objectType.GetProperties(
BindingFlags.Public | BindingFlags.Instance);
return allProps.Where(p => p.PropertyType.IsPrimitive ||
p.PropertyType == typeof(string)).ToArray();
}
private class PropertyComparer : IEqualityComparer<PropertyInfo>
{
public bool Equals(PropertyInfo x, PropertyInfo y)
{
return x.Name.Equals(y.Name);
}
public int GetHashCode(PropertyInfo obj)
{
return obj.Name.GetHashCode();
}
}
And here's an example of a way I would call it, with sample classes:
public class Foo
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
public class FooOther
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
var foo = new Foo { IntProp = 1, StringProp = "foo" };
var mappedFoo = Map<Foo, FooOther>(foo);
About the only hint I've gotten out of Visual Studio is from the watch window: if the property type is a string, the watch window reports the type of convertedValue as object. If the property type is an int, the watch window reports object {int}.
The PropertyInfo you are using is still coupled to the type the property it is representing is a member of, so you aren't able to use it to set the value of an object of another type without the error you are getting.
Here's a shortened example of the behavior:
public class A {
public string Id {get;set;}
}
public class B {
public string Id {get;set;}
}
void Main()
{
var test = new A() { Id = "Test"};
var prop = test.GetType().GetProperty("Id");
var b = (B)Activator.CreateInstance(typeof(B));
var fromValue = prop.GetValue(test);
var converted = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(b, converted, null); // Exception
}
This makes sense if you think of the PropertyInfo as a member of A. To fix this, you'll want to get a property info that's specific to your type. I can fix up my example with the following:
var propTo = typeof(B).GetProperty(prop.Name);
propTo.SetValue(b, converted, null);
Console.WriteLine(b.Id); // Output: Test
Bringing that together, if you change the contents of your foreach to the following you should be in the clear:
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
var propTo = typeof(TTO).GetProperty(prop.Name);
propTo.SetValue(returnObject, convertedValue, null);
}
I have a generic list. I have to filter the list based on the value of the property of list item. Item type is not known until runtime.
I have to find the item type by reflection and then need to filter.
Please help me by any idea or example
Thanks.
I Hope this help
pass any list, and specify a property name, and a filter method
private IList FilterList(IList list, string propName, Predicate<object> filterMethod) {
var result = new List<object>();
foreach (var item in list) {
var value = item.GetType().GetProperty(propName).GetValue(item);
if (filterMethod(value)) {
result.Add(item);
}
}
return result;
}
Example :
var result = FilterList(list, "Age", age => (int)age >= 18);
you can develop it and make it fully dynamic
If the list is generic you should know at least a base type at compile time.
If the base type contains the property - just use it.
If only some of the subtypes(ideally one) contain the property you're interested in you can do a cast on the list and then use your property.
list.Cast<Derived>().Select(i => i.Property == "val");
If that is not ok(say the type of the list has many derived types that contain the property and others that do not), you can use dynamic with a try/catch block.
list.Select(i => {
try
{
dynamic item = i;
return item.Prop == "value";
}
catch(RuntimeBinderException) //this type doesn't contain the property
{
return false;
}
});
Try this. It can determine whether a List is a list of some specified type. Based on that you should be able to filter for any specific type.
public class TypeA
{ }
public class TypeB
{ }
public class GenericFilter
{
public bool IsOfType<T>(IEnumerable<dynamic> objectToInspect)
{
var genericType = objectToInspect.GetType().GenericTypeArguments[0];
return genericType == typeof(T);
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void IdentifiesExpectedType()
{
var classToInspect = new List<TypeA>();
var filter = new GenericFilter();
Assert.IsTrue(filter.IsOfType<TypeA>(classToInspect));
}
[TestMethod]
public void RejectsNonMatchingType()
{
var classToInspect = new List<TypeA>();
var filter = new GenericFilter();
Assert.IsFalse(filter.IsOfType<TypeB>(classToInspect));
}
}
This works just as well:
public bool IsOfType<T>(IEnumerable<dynamic> listToFilter)
{
return (listToFilter as IEnumerable<T>) != null;
}
This will take a list of lists and group them into a Dictionary<Type, List<List<dynamic>> where all of the lists for each key are of that type. That way you can select lists of type T just by retrieving them from the dictionary.
public Dictionary<Type, List<List<dynamic>>> GroupListsByType(List<List<dynamic>> lists)
{
var types = lists.Select(list => list.GetType().GenericTypeArguments[0]).Distinct().ToList();
var grouped = new Dictionary<Type, List<List<dynamic>>>();
types.ForEach(type =>
{
grouped.Add(type, new List<List<dynamic>>());
grouped[type].AddRange(lists.Where(list=>list.GetType().GenericTypeArguments[0] == type));
});
return grouped;
}
(I'm not questioning the "why." I don't know if I would recommend this, but it's interesting to write.)
I'm playing with PropertyDescriptor and ICustomTypeDescriptor (still) trying to bind a WPF DataGrid to an object, for which the data is stored in a Dictionary.
Since if you pass WPF DataGrid a list of Dictionary objects it will auto generate columns based on the public properties of a dictionary (Comparer, Count, Keys and Values) my Person subclasses Dictionary and implements ICustomTypeDescriptor.
ICustomTypeDescriptor defines a GetProperties method which returns a PropertyDescriptorCollection.
PropertyDescriptor is abstract so you have to subclass it, I figured I'd have a constructor that took Func and an Action parameters that delegate the getting and setting of the values in the dictionary.
I then create a PersonPropertyDescriptor for each Key in the dictionary like this:
foreach (string s in this.Keys)
{
var descriptor = new PersonPropertyDescriptor(
s,
new Func<object>(() => { return this[s]; }),
new Action<object>(o => { this[s] = o; }));
propList.Add(descriptor);
}
The problem is that each property get's its own Func and Action but they all share the outer variable s so although the DataGrid autogenerates columns for "ID","FirstName","LastName", "Age", "Gender" they all get and set against "Gender" which is the final resting value of s in the foreach loop.
How can I ensure that each delegate uses the desired dictionary Key, i.e. the value of s at the time the Func/Action is instantiated?
Much obliged.
Here's the rest of my idea, I'm just experimenting here these are not 'real' classes...
// DataGrid binds to a People instance
public class People : List<Person>
{
public People()
{
this.Add(new Person());
}
}
public class Person : Dictionary<string, object>, ICustomTypeDescriptor
{
private static PropertyDescriptorCollection descriptors;
public Person()
{
this["ID"] = "201203";
this["FirstName"] = "Bud";
this["LastName"] = "Tree";
this["Age"] = 99;
this["Gender"] = "M";
}
//... other ICustomTypeDescriptor members...
public PropertyDescriptorCollection GetProperties()
{
if (descriptors == null)
{
var propList = new List<PropertyDescriptor>();
foreach (string s in this.Keys)
{
var descriptor = new PersonPropertyDescriptor(
s,
new Func<object>(() => { return this[s]; }),
new Action<object>(o => { this[s] = o; }));
propList.Add(descriptor);
}
descriptors = new PropertyDescriptorCollection(propList.ToArray());
}
return descriptors;
}
//... other other ICustomTypeDescriptor members...
}
public class PersonPropertyDescriptor : PropertyDescriptor
{
private Func<object> getFunc;
private Action<object> setAction;
public PersonPropertyDescriptor(string name, Func<object> getFunc, Action<object> setAction)
: base(name, null)
{
this.getFunc = getFunc;
this.setAction = setAction;
}
// other ... PropertyDescriptor members...
public override object GetValue(object component)
{
return getFunc();
}
public override void SetValue(object component, object value)
{
setAction(value);
}
}
Simply:
foreach (string s in this.Keys)
{
string copy = s;
var descriptor = new PersonPropertyDescriptor(
copy,
new Func<object>(() => { return this[copy]; }),
new Action<object>(o => { this[copy] = o; }));
propList.Add(descriptor);
}
With captured variables, it is where it is declared that is important. So by declaring the captured variable inside the loop, you get a different instance of the capture-class per iteration (the loop variable, s, is technically declared outside the loop).
Marc's solution is of course correct, but I thought I'd expand upon WHY below. As most of us know, if you declare a variable in a for or foreach statement, it only lives as long as what's inside, which makes it seem like the variable is the same as a variable declared in the statement-block of such a statement, but that's not right.
To understand it better, take the following for-loop. Then I'll re-state the "equivalent" loop in a while-form.
for(int i = 0; i < list.Length; i++)
{
string val;
list[i] = list[i]++;
val = list[i].ToString();
Console.WriteLine(val);
}
This works out to in while-form like below: (it isn't exactly the same, because continue will act differently, but for scoping rules, it's the same)
{
int i = 0;
while(i < list.Length)
{
{
string val;
list[i] = list[i]++;
val = list[i].ToString();
Console.WriteLine(val);
}
i++;
}
}
When "exploded" out this way, the scope of the variables becomes clearer, and you can see why it always captures the same "s" value in your program, and why Marc's solution shows where to place your variable so that a unique one is captured every time.
create a local copy of s inside your for loop and use that.
for(string s in this.Keys) {
string key = s;
//...
}
For some additional thoughts on this issue see
http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/
I have an enumeration for my Things like so:
public enum Things
{
OneThing,
AnotherThing
}
I would like to write an extension method for this enumeration (similar to Prise's answer here) but while that method works on an instance of the enumeration, ala
Things thing; var list = thing.ToSelectList();
I would like it to work on the actual enumeration instead:
var list = Things.ToSelectList();
I could just do
var list = default(Things).ToSelectList();
But I don't like the look of that :)
I have gotten closer with the following extension method:
public static SelectList ToSelectList(this Type type)
{
if (type.IsEnum)
{
var values = from Enum e in Enum.GetValues(type)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
else
{
return null;
}
}
Used like so:
var list = typeof(Things).ToSelectList();
Can we do any better than that?
Extension methods only work on instances, so it can't be done, but with some well-chosen class/method names and generics, you can produce a result that looks just as good:
public class SelectList
{
// Normal SelectList properties/methods go here
public static SelectList Of<T>()
{
Type t = typeof(T);
if (t.IsEnum)
{
var values = from Enum e in Enum.GetValues(type)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
return null;
}
}
Then you can get your select list like this:
var list = SelectList.Of<Things>();
IMO this reads a lot better than Things.ToSelectList().
No.
The best you can do is put it on a static class, like this:
public static class ThingsUtils {
public static SelectList ToSelectList() { ... }
}
Aaronaught's answer is really great, based on that I made the following implementation:
public class SelectList
{
public static IEnumerable<Enum> Of<T>() where T : struct, IConvertible
{
Type t = typeof(T);
if (t.IsEnum)
{
return Enum.GetValues(t).Cast<Enum>();
}
throw new ArgumentException("<T> must be an enumerated type.");
}
}
In my opinion it's a little bit safer, as you can - almost - call it only with Enums, and of course instead of the throw you can simply return null if you want an exception-free version.
I use 'Type' instead of 'Enum' to add extension. Then I can get any type of list back from the method. Here it returns string values:
public static string[] AllDescription(this Type enumType)
{
if (!enumType.IsEnum) return null;
var list = new List<string>();
var values = Enum.GetValues(enumType);
foreach (var item in values)
{
// add any combination of information to list here:
list.Add(string.Format("{0}", item));
//this one gets the values from the [Description] Attribute that I usually use to fill drop downs
//list.Add(((Enum) item).GetDescription());
}
return list.ToArray();
}
Later I could use this syntax to get what I want:
var listOfThings = typeof (Things).AllDescription();
#Aaronaught has a very good answer. To extend his answer, you can also even make it more generic. I have this in a global library...
public static IQueryable GetAllEnumValues<T>()
{
IQueryable retVal = null;
Type targetType = typeof(T);
if(targetType.IsEnum)
{
retVal = Enum.GetValues(targetType).AsQueryable();
}
return retVal;
}
Now you have de-coupled this functionality from the SelectList class. So you can call this in your SelectList methods, or anywhere else for that matter.
public class SelectList
{
public static SelectList Of<T>
{
IQueryable enumValues = GetAllEnumValues<T>();
var values =
from Enum e in enumValues
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
}
In my opinion, this is the cleanest way. Why?
It works for any System.Enum
The extension method itself is cleaner.
To call it you just add new and that's a small trade off (because it has to have an instance in order to work.
You aren't passing null around and it literally won't compile if you try to use it with another type.
Usage:
(new Things()).ToSelectList()
Extension Method:
[Extension()]
public SelectList ToSelectList(System.Enum source)
{
var values = from Enum e in Enum.GetValues(source.GetType)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
The closest you can come, I think, is to dummy things up a bit to work like an enum without being one. Here's what I've come up with--it seems like a lot of work just to plop a static method on an enumeration, although I do understand the programming appeal of it:
public class PseudoEnum
{
public const int FirstValue = 1;
private static PseudoEnum FirstValueObject = new PseudoEnum(1);
public const int SecondValue = 2;
private static PseudoEnum SecondValueObject = new PseudoEnum(2);
private int intValue;
// This prevents instantation; note that we cannot mark the class static
private PseudoEnum() {}
private PseudoEnum(int _intValue)
{
intValue = _intValue;
}
public static implicit operator int(PseudoEnum i)
{
return i.intValue;
}
public static implicit operator PseudoEnum(int i)
{
switch (i)
{
case FirstValue :
return FirstValueObject;
case SecondValue :
return SecondValueObject;
default:
throw new InvalidCastException();
}
}
public static void DoSomething(PseudoEnum pe)
{
switch (pe)
{
case PseudoEnum.FirstValue:
break;
case PseudoEnum.SecondValue:
break;
}
}
}