Set indexed value by reflection gives me TargetParameterCountException - c#

I have a PolygonRenderer class containing a Vertices property, which is a List, holding the points of the polygon the class renders.
When I try to change a specific point in this list by reflection, I get a System.Reflection.TargetParameterCountException on the last line of my function :
public override void ApplyValue(string property, object value, int? index)
{
List<PropertyInfo> properties = Data.GetType().GetProperties().ToList();
PropertyInfo pi = properties.FirstOrDefault(p => p.Name == property);
pi.SetValue(Data, value,
index.HasValue ? new object[] { index.Value } : null);
}
When I debug, I get index.Value = 3, Data is the PolygonRenderer instance and pi reflects the Vertices property, which count = 4.
Since my index is supposed to be the last item of my list, how is it possible that I get a count exception on that property ?
Thanks

I have a PolygonRenderer class containing a Vertices property, which is a List...
So you need to execute something like this
Data.Vertices[index] = value
and what your code is trying to do is
Data[index] = value
You can use something like this instead
public override void ApplyValue(string property, object value, int? index)
{
object target = Data;
var pi = target.GetType().GetProperty(property);
if (index.HasValue && pi.GetIndexParameters().Length != 1)
{
target = pi.GetValue(target, null);
pi = target.GetType().GetProperties()
.First(p => p.GetIndexParameters().Length == 1
&& p.GetIndexParameters()[0].ParameterType == typeof(int));
}
pi.SetValue(target, value, index.HasValue ? new object[] { index.Value } : null);
}

Related

Cycle and access Method's parameters value

I have the following Method (I exemplify what I need in the comments of the method):
public static Dictionary<int, int> Foo(bool os, bool rad, bool aci, bool outr, string distrito = null)
{
if (os == false && rad == false && aci == false && outr == false)
{
return new Dictionary<int, int>();
}
var parameters = MethodBase.GetCurrentMethod().GetParameters();
foreach (ParameterInfo parameter in parameters)
{
// I would love if parameter.Value existed, because:
// if (parameter.Value==true) {
// x++
// if (x == 1) string s = "true" + parameter.Name;
// if (x > 1) s += "Additional true" + parameter.Name;
// }
// s += "End";
}
return null;
}
I have to know if one or more values of the bool parameters are true. Since they are four, imagine the combination of if I would have to do to check if only one is true, or if more than one, which are true.
So, how can I cycle the current value of the incoming Method parameters without using the parameter variable itself?
If you only want to know how many are true, you can turn them into integers and add their values:
var values = new[] {os, rad, aci, outr};
var sum = values.Select(v => Convert.ToInt32(v)).Sum();
If you need the name of the parameters, then you can create an anonymous object and read its properties:
public static Dictionary<int, int> Foo(bool os, bool rad, bool aci, bool outr, string distrito = null)
{
var obj = new { os, rad, aci, outr};
foreach (PropertyInfo pInfo in obj.GetType().GetProperties())
{
var value = (bool)pInfo.GetValue(obj);
if (value)
{
//Do whatever you want here.
Console.WriteLine("{0}: {1}", pInfo.Name, value);
}
}
}
You can try some LINQ extensions, I think composition of Where and Select may be a solution, with a string.Join method on top:
// get all parameters
var parameters = MethodBase.GetCurrentMethod().GetParameters();
// for each true parameter we add this prefix
var truePrefix = "true";
// we put this string between true parameters, if more than one are present
var separator = "Additional";
// Join IEnumerable of strings with a given separator
var total = string.Join(separator, parameters
// filter parameters to check only boolean ones, and only true ones
.Where(p => p.ParameterType == typeof(bool) && (bool)p.Value)
// select a name with prefix
.Select(p => truePrefix + p.Name)))

GetValue of a property

I have a table in an MSSQL database with ~300 columns and I want to extract only one row and get all the values of the columns. I've used this code but I have problems with GetValue(,) method. This is my code:
private PropertyInfo[] GetValuesDB()
{
......
var result = from val in datacTx.TableA
where val.A == "AA" + "-" + "11" &&
val.B == "CC
select val;
return result.First().GetType().GetProperties();
}
...
public void MethodA()
{
var res = GetValuesDB();
foreach (var propertyInfo in res)
{
var rez = propertyInfo.GetValue(res,null);
}
}
I always obtain this kind of exception:
Object does not match target type.
GetValue expects the type of the first argument to be the type declaring the property (or a subtype).
So your result.First() call returns a reference to an object - and you want to get the properties on that object... so that should be the reference you pass to GetValue. You'll need to change your code to return that reference:
// Ideally, change this to a more appropriate return type...
private object GetValuesDB()
{
......
var result = from val in datacTx.TableA
where val.A == "AA" + "-" + "11" &&
val.B == "CC"
select val;
return result.First();
}
...
public void MethodA()
{
var res = GetValuesDB();
foreach (var propertyInfo in res.GetType().GetProperties())
{
var rez = propertyInfo.GetValue(res, null);
}
}
So GetValuesDB now returns a reference to the relevant entity - and then you fetch the property descriptors, and ask each descriptor for the value of that property on that object.

Order by anonymous method

With respect to How to cast while Sorting?, I tried one of the answers below.
vehicleList.OrderBy(c=>
{
Car car = c as Car;
if (car != null)
return car.ModelName
else
return "";
}
But it gives compiler error:
The type arguments for method
'System.Linq.Enumerable.OrderBy(System.Collections.Generic.IEnumerable,
System.Func)' cannot be inferred from the usage. Try
specifying the type arguments explicitly.
What is wrong in this? How to correct?
What I try to achieve is as below:
Sort by ModelName if the object is of type Car
Else if object id of type Train, then by TrainName.
This solves the error:
var sortedVehicles = vehicleList.OrderBy<Vehicle, string>(c=>
{
...
});
You can ipmplement a comparer like this:
var sorted = uiItems.OrderBy(x => x.Contact, new YourComparer ());
public class YourComparer : IComparer<Contact>
{
public int Compare(Contact? x, Contact? y)
{
if (x == y)
return 0;
if (x == null)
return 1;
if (y == null)
return -1;
if (x.Presence == null)
return 1;
if (y.Presence == null)
return -1;
return return x.Presence < y.Presence ? -1 : 1;
}
}
Did you try the suggestion in the question you referenced, to use a Linq Cast?
var sorted = uiItems.Cast<Contact>()
.OrderBy(c => contact.Presence; );
Look at the below example:
class Item
{
public int X;
public string Y;
}
var items = new Item[100];
var ordered = items.OrderBy<Item, string>(i => i.Y);
OrderBy requires two types. The former is of the enumerable and the latter is used in lambda expression. Additionaly in some cases, the elements of collection that is being used, must be casted. You can use Cast method prior to OrderBy if you are sure, that all types are of a particular type or OfType in order to ommit other types.

Why reflection does not find property

I have the class:
class Person
{
public string Name { get { return "Antonio"; } }
}
and the Code:
IEnumerable<object> uncknownObject;
uncknownObject = new ObservableCollection<Person>( );
var observCol = uncknownObject.GetType( );
var x = ( ( dynamic )observCol ).GenericTypeArguments[ 0 ];
var y = observCol.GetProperty( "GenericTypeArguments" );
var instance = ( Person )Activator.CreateInstance( x );
Console.WriteLine( instance.Name ); // Print Antonio!!!
why does y == null ?
Note the picture:
the debugger shows that the property GenericTypeArguments should exist and the code shows the opossite. It can be proven that the debugger is right and that property exist because then how come x is not null. If that property exists then why y is equal to null!!!???
Edit
Thanks to Ani I now have:
IEnumerable<object> uncknownObject;
uncknownObject = new ObservableCollection<Person>();
var observCol = uncknownObject.GetType();
var genTypeArgsProperty = typeof(Type).GetProperty("UnderlyingSystemType");
var genTypeArgsValue = (genTypeArgsProperty.GetValue(observCol, null));
var f = genTypeArgsValue.GetType().GetMethod("GetGenericArguments");
IEnumerable<object> result = (IEnumerable<object>)f.Invoke(genTypeArgsValue, null);
var x = result.FirstOrDefault();
var instance = Activator.CreateInstance( (Type)x );
In case of curios why I needed that functionality click here
I don't really understand what you're trying to accomplish with all this meta-meta-reflection, but you seem to have misunderstood what Type.GetProperty does. It gets meta-data for a property on the actual type represented by the System.Type instance (in this case, ObservableCollection<Person>). It does not get meta-data for a property declared on System.Type itself, unless of course you call it on a System.Type representing System.Type itself.
In your case, y is null since ObservableCollection<Person> does not have a property named "GenericTypeArguments".
Try this instead:
var genTypeArgsProperty = typeof(Type).GetProperty("GenericTypeArguments");
var genTypeArgsValue = (Type[]) (genTypeArgsProperty.GetValue(observCol, null));
var onlyTypeArgValue = genTypeArgsValue.Single();
This code works with net framework 4:
IEnumerable<object> uncknownObject;
uncknownObject = new ObservableCollection<Person>();
var observCol = uncknownObject.GetType();
var x = ((dynamic) observCol).UnderlyingSystemType.GetGenericArguments()[0];
var y = observCol.GetGenericArguments();
var instance = (Person)Activator.CreateInstance(x);
Console.WriteLine(instance.Name); // Print Antonio!!!

Conditions inside Select clause in Linq

I have a method that gets a list of objects and then returns all their properties values based on the attributes and types of the property.
I'm having difficulties with selecting the value because the Select depends on conditions.
What I did so far allows me to get the value in the ugliest way I have ever seen:
public IEnumerable<string> GetValues(List<ProperyInfo> objects)
{
var allValues = new List<string>();
foreach (var obj in objects)
{
// Get the PropertyInfo for the obj
var properties = GetPropertiesWithTheAttributes(obj);
var values = properties.Select(x => new
{
Value = x.GetValue(obj, null) == null
? string.empty
:
x.PropertyType.BaseType == typeof(Enum)
? Convert.ToInt32(x.GetValue(obj, null)).ToString()
: (x.GetValue(obj, null)).ToString(),
((ReportAttribute)
Attribute.GetCustomAttribute(x, typeof(ReportAttribute), false)).Order
})
.OrderBy(x => x.Order)
.Select(x => x.Value.ToString())
.ToList();
allValues.AddRange(values);
}
return allValues;
}
And in the code I published here I even removed the check for the DisplayDate property in the ReportAttribute, which verifies if the datetime property of the attribute would be displayed as date or datetime...
serenity now!
I would simply extract this into 2 methods, and get rid of the ternary operator:
// not sure how to name 'obj' and 'x' without more context
private static object GetValue(PropertyInfo obj, PropertyInfo x)
{
if (x.GetValue(obj, null) == null) return string.Empty;
if (x.PropertyType.BaseType == typeof (Enum))
return Convert.ToInt32(x.GetValue(obj, null));
return x.GetValue(obj, null);
}
private static int GetOrder(PropertyInfo x)
{
return ((ReportAttribute) Attribute.GetCustomAttribute(x, typeof(ReportAttribute), false)).Order;
}
So you can write:
public IEnumerable<string> GetValues(List<PropertyInfo> objects)
{
var allValues = new List<string>();
foreach (var obj in objects)
{
// Get the PropertyInfo for the obj
var properties = GetPropertiesWithTheAttributes(obj);
var values = properties.Select(x => new
{
Value = GetValue(obj, x),
Order = GetOrder(x)
})
.OrderBy(x => x.Order)
.Select(x => x.Value.ToString())
.ToList();
allValues.AddRange(values);
}
return allValues;
}
If you really want to inline this, you can change the lambda expression in your Select to a statement:
.Select(x =>
{
object value;
if (x.GetValue(obj, null) == null) value = string.Empty;
else if (x.PropertyType.BaseType == typeof (Enum))
value = Convert.ToInt32(x.GetValue(obj, null));
else value = x.GetValue(obj, null);
return new
{
Value = value,
((ReportAttribute)
Attribute.GetCustomAttribute(x, typeof (ReportAttribute), false)).
Order
};
})

Categories

Resources