I have this below piece of code, which i always highlighted by SONAR as a MAJOR issue, because of violation of a rule called with the below message.
Multiple (3) calls to virtual property 'System.String System.Reflection.MemberInfo::get_Name()'.
And the rule description says
AvoidRepetitiveCallsToPropertiesRule
gendarme : AvoidRepetitiveCallsToPropertiesRule
The rule warn if virtual, or unlikely to be inline-able, property getters are called several times by a method. In most cases repetitive calls simply requires more time without any gains since the result will always be identical. You should ignore the reported defects if a different value is expected each time the property is called (e.g. calling DateTime.Now).**
private static void OverrideConfigurationValues(ConfigA configa,
ConfigB configb, ConfigC configc)
{
Type t = configa();
var properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var overriddenvalues = new Dictionary<string, object>();
foreach (var prop in properties)
{
var value = prop.GetValue(configa,null);
if (value != null)
{
overriddenvalues.Add(prop.Name, value);
}
}
Type b = configb.GetType();
foreach (var prop in b.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!overriddenvalues.ContainsKey(prop.Name))
{
var value = prop.GetValue(b,null);
if (value != null)
{
overriddenvalues.Add(prop.Name, value);
}
}
}
foreach (var overriddenvalue in overriddenvalues)
{
var overriden = overriddenvalue;
foreach (var prop in configa.GetType().GetProperties().Where(prop => prop.Name == overriden.Key))
{
prop.SetValue(configa, overriddenvalue.Value,null);
}
}
}
If the SONAR is complaining about the line prop.Name which i have inside the foreach loop? How can i avoid it?
Ron Beyer's comments are proper answers to this question.
So, your code would look like this based on his comments:
...
foreach (var prop in b.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var propName = prop.Name;
if (!overriddenvalues.ContainsKey(propName))
{
var value = prop.GetValue(b,null);
if (value != null)
{
overriddenvalues.Add(propName, value);
}
}
}
...
Note that the support of Gendarme rules was dropped in the 3.0 version of the C# plugin.
Related
I have a generic object comparison method which I use to compare two models with the same structure.
public static List<Variance> DetailedCompare<T>(this T val1, T val2)
{
var variances = new List<Variance>();
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties.Where(t => t.IsMarkedWith<IncludeInComparisonAttribute>()))
{
var v = new Variance
{
PropertyName = property.Name,
ValA = property.GetValue(val1, null),
ValB = property.GetValue(val2, null)
};
if (v.ValA == null && v.ValB == null) { continue; }
if (v.ValA != null && !v.ValA.Equals(v.ValB))
{
variances.Add(v);
}
}
return variances;
}
The problem I have is that sometimes an object is passed to it that may contain a list of other objects within it. Because it only compares at the top level it just returns that the object array was changed. Ideally I would like it to go through the nested array and look at the changed values as well.
Ideally I think it should probably make a recursive call when it finds an object array. Any ideas how I might go about this?
Edit - with working examples
Here are some .net fiddle examples of how this is meant to work.
This is the first code example that doesn't search down through the nested objects and just reports that the collection has changed (as per the code above):
https://dotnetfiddle.net/Cng7GI
returns:
Property: NumberOfDesks has changed from '5' to '4'
Property: Students has changed from 'System.Collections.Generic.List1[Student]' to 'System.Collections.Generic.List1[Student]'
Now if I try and call the DetailedCompare if I find a nested array using:
if (v.ValA is ICollection)
{
Console.WriteLine("I found a nested list");
variances.AddRange(v.ValA.DetailedCompare(v.ValB));
}
else if(v.ValA != null && !v.ValA.Equals(v.ValB)){
variances.Add(v);
}
it doesn't look like the recursive call works
https://dotnetfiddle.net/Ns1tx5
as I just get:
I found a nested list
Property: NumberOfDesks has changed from '5' to '4'
If I add:
var list = v.ValA.DetailedCompare<T>(v.ValB);
inside the Collection check, I get an error that:
object does not contain a definition for 'DetailedCompare' ... Cannot convert instance argument type 'object' to T
really what I want from it is just a single array of all the property names and their value changes.
Property: NumberOfDesks has changed from '5' to '4'
Property: Id has changed from '1' to '4'
Property: FirstName has changed from 'Cheshire' to 'Door'
etc
Calling the method recursively is the issue here.
If we call a method DetailedCompare recursively passing as parameters two objects all its fine - as we can get their properties and compare them.
However when we call DetailedCompare recursively passing a two list of objects - we can not just get the properties of those lists - but we need to traverse and get the properties of those list and compare their value.
IMHO it would be better to separate the logic using a helper method - so when we find a nested list - we can tackle the logic as I have described above.
This is the Extension class I have written
public static class Extension
{
public static List<Variance> Variances { get; set; }
static Extension()
{
Variances = new List<Variance>();
}
public static List<Variance> DetailedCompare<T>(this T val1, T val2)
{
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var v = new Variance
{
PropertyName = property.Name,
ValA = property.GetValue(val1, null),
ValB = property.GetValue(val2, null)
};
if (v.ValA == null && v.ValB == null)
{
continue;
}
if (v.ValA is ICollection)
{
Console.WriteLine("I found a nested list");
DetailedCompareList(v.ValA,v.ValB);
}
else if (v.ValA != null && !v.ValA.Equals(v.ValB))
{
Variances.Add(v);
}
}
return Variances;
}
private static void DetailedCompareList<T>(T val1, T val2)
{
if (val1 is ICollection collection1 && val2 is ICollection collection2)
{
var coll1 = collection1.Cast<object>().ToList();
var coll2 = collection2.Cast<object>().ToList();
for (int j = 0; j < coll1.Count; j++)
{
Type type = coll1[j].GetType();
PropertyInfo[] propertiesOfCollection1 = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
PropertyInfo[] propertiesOfCollection2 = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < propertiesOfCollection1.Length; i++)
{
var variance = new Variance
{
PropertyName = propertiesOfCollection1[i].Name,
ValA = propertiesOfCollection1[i].GetValue(coll1[j]),
ValB = propertiesOfCollection2[i].GetValue(coll2[j])
};
if (!variance.ValA.Equals(variance.ValB))
{
Variances.Add(variance);
}
}
}
}
}
}
With the following result:
Limitations
This approach is bounded by the definition of your objects - hence it can only work with 1 level of depth.
I have to cast enum but I want this to be as generic as possbile. How can I replace the Cast<XX>() part with propertyType?
foreach (var prop in MainType.GetProperties().Where(x => x.PropertyType.IsEnum))
{
var x = new { name = prop.Name, values = new List<object>() };
foreach (var v in Enum.GetValues(prop.PropertyType).Cast<XX>())
x.values.Add(new { p = v.GetAttribute<DescriptionAttribute>().Description,
c = v.GetAttribute<DefaultValueAttribute>().Value });
o.Add(x);
}
public static TAttribute GetAttribute<TAttribute>(this Enum value) where TAttribute : Attribute
{
var type = value.GetType();
var name = Enum.GetName(type, value);
return type.GetField(name)
.GetCustomAttributes(false)
.OfType<TAttribute>()
.SingleOrDefault();
}
public enum JobApplicationState : short
{
[Description("Active")]
[DefaultValue(typeof(string), "bg-primary text-highlight")]
Active = 1,
[Description("Passive")]
[DefaultValue(typeof(string), "bg-grey text-highlight")]
Passive = 2,
[Description("Rejected")]
[DefaultValue(typeof(string), "bg-danger text-highlight")]
Rejected = 3,
[Description("Accepted")]
[DefaultValue(typeof(string), "bg-success text-highlight")]
Accepted = 4
}
THIS WORKED!
foreach (MemberInfo m in prop.PropertyType.GetFields())
{
if (m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault() != null && m.GetCustomAttributes(typeof(DefaultValueAttribute), true).FirstOrDefault() != null)
{
x.values.Add(
new
{
p = ((DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault()).Description,
c = ((DefaultValueAttribute)m.GetCustomAttributes(typeof(DefaultValueAttribute), true).FirstOrDefault()).Value
});
}
}
You should realize that an enum is nothing more than a named wrapper around an integral value. I've described some details on how enums work here: Cast int to enum in C# .
The question here is to get attributes values from an enum. As you might imagine, this question is about getting attributes from the type, not about getting attributes from the value (there's no such thing). Still, if you call a method like void Foo<T>(T myEnum), the type T will hold all the necessary info, even though 'in real life' the value of myEnum is passed around as an integral type.
From this you can also deduce that you're actually looking for the attributes of the MemberInfo's of T, since you're looking for the members of the type (e.g. the enum). Again, all the details are in my post on how enums work. Hence the answer:
foreach (MemberInfo m in prop.PropertyType.GetFields())
{
// use m.GetAttribute(...)
}
Why don´t you simply cast to Enum:
foreach (var v in Enum.GetValues(prop.PropertyType).Cast<Enum>())
Now you can call your extension-method on every element within the returned list.
v.GetAttribute<MyType>()
I want to convert DataRow To Object. I write 1 class to do this.
Error like this:
No overload for method 'SetValue' takes 2 arguments
No overload for method 'GetValue' takes 1 arguments
But I can't using GetValues() and SetValues(). When converting project to 4.5. It's work.
My project is set platform target is 3.5 (Obligatory - because I connect with the device must using .NET 3.5).
How to fix this?
Here my code:
public DataRowToObject(DataRow row)
{
List<PropertyInfo> listProperty = this.GetProperties();
foreach (PropertyInfo prop in listProperty)
{
if (!row.Table.Columns.Contains(prop.Name) ||
row[prop.Name] == null ||
row[prop.Name] == DBNull.Value)
{
prop.SetValue(this, null);
continue;
}
try
{
object value = Convert.ChangeType(row[prop.Name], prop.PropertyType);
prop.SetValue(this, value);
}
catch
{
prop.SetValue(this, null);
}
}
}
public virtual Hashtable GetParameters()
{
Type type = this.GetType();
List<PropertyInfo> listProperty = new List<PropertyInfo>(type.GetProperties());
Hashtable result = new Hashtable();
foreach (PropertyInfo prop in listProperty)
{
result.Add(prop.Name, prop.GetValue(this));
}
return result;
}
There is an overload for PropertyInfo.SetValue and PropertyInfo.GetValue without the indexer added in .NET 4.5.
But it's simply a matter of passing null to the indexer parameter on previous versions (using this and this overloads).
So:
prop.SetValue(this, value, null);
And
prop.GetValue(this, null);
This should work on .NET .3.5 (up to recent versions)... actually for NET 2.0 and up :-)
Currently, I am using Reflection and want to know if any other way to achieve the same.
foreach (var propertyInfo in MyObject.GetType().GetProperties())
{
if (propertyInfo.CanRead)
{
if (propertyInfo.Name.Equals(fieldName))
{
propertyInfo.SetValue(MyObject, "Some value", null);
break;
}
}
}
You can use a quicker version of reflection than what you have (the loop is not necessary):
var propertyInfo = MyObject.GetType().GetProperty(fieldName);
if (propertyInfo != null && propertyInfo.CanRead)
propertyInfo.SetValue(MyObject, "Some value", null);
Beyond reflection, I believe you can emit IL code directly -- take a look at "FastMember" (although I am not sure if Silverlight can support this approach).
If all you have is a string of the property name then I don't know any better way then reflection but looping on the posted code is not efficient.
HashSet<String> properties = new HashSet<String>();
// add the properties
foreach (var propertyInfo in MyObject.GetType().GetProperties())
{
if (propertyInfo.CanRead)
{
if (properties.Contains(propertyInfo.Name))
{
propertyInfo.SetValue(MyObject, "Some value", null);
}
}
}
I'm working in a project to get some information about Issuers. In MainWindow activity I have this line of code:
builder.AddCustomAttributes(typeof(IssuerActivity), new DesignerAttribute(typeof(IssuerDesigner)));
So I have a IssuerDesigner that I put in a listbox all of the Issuers in his contructor method and I save in an Issuer [] all of these.
Now, when I execute this rehosted workflow, I need to send this Issuer[] to IssuerActivity to analized in a foreach task everyone of them...
The question is: ¿What have I to do to for IssuerActivity gets Issuer[] that It's assigned in IssuerDesigner?
Your question is hard to follow but I beleive this is what you are looking for. You will have to use Reflection on your object to be able to grab the Attribute Values. It should be something like this.
MemberInfo[] members = builder.GetType().GetProperties();
foreach (MemberInfo m in members)
{
if (m.MemberType == MemberTypes.Property)
{
PropertyInfo p = m as PropertyInfo;
object[] attribs = p.GetCustomAttributes(false);
foreach (object attr in attribs)
{
IssuerDesigner d = attr as IssuerDesigner;
if (d != null)
{
foreach(object obj in d.Issuer)
{
DoSomething(obj);
}
}
}
}
}