Foreach enum values without casting - c#

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>()

Related

c# Object Comparison With Complex Objects

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.

Detect if an object is a ValueTuple

I have a use case where I need to check if a value is a C# 7 ValueTuple, and if so, loop through each of the items. I tried checking with obj is ValueTuple and obj is (object, object) but both of those return false. I found that I could use obj.GetType().Name and check if it starts with "ValueTuple" but that seems lame to me. Any alternatives would be welcomed.
I also have the issue of getting each item. I attempted to get Item1 with the solution found here: How do I check if a property exists on a dynamic anonymous type in c#? but ((dynamic)obj).GetType().GetProperty("Item1") returns null. My hope was that I could then do a while to get each item. But this does not work. How can I get each item?
Update - more code
if (item is ValueTuple) //this does not work, but I can do a GetType and check the name
{
object tupleValue;
int nth = 1;
while ((tupleValue = ((dynamic)item).GetType().GetProperty($"Item{nth}")) != null && //this does not work
nth <= 8)
{
nth++;
//Do stuff
}
}
Structures do not inherit in C#, so ValueTuple<T1>, ValueTuple<T1,T2>, ValueTuple<T1,T2,T3> and so on are distinct types that do not inherit from ValueTuple as their base. Hence, obj is ValueTuple check fails.
If you are looking for ValueTuple with arbitrary type arguments, you can check if the class is ValueTuple<,...,> as follows:
private static readonly Set<Type> ValTupleTypes = new HashSet<Type>(
new Type[] { typeof(ValueTuple<>), typeof(ValueTuple<,>),
typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>), typeof(ValueTuple<,,,,,,,>)
}
);
static bool IsValueTuple2(object obj) {
var type = obj.GetType();
return type.IsGenericType
&& ValTupleTypes.Contains(type.GetGenericTypeDefinition());
}
To get sub-items based on the type you could use an approach that is not particularly fast, but should do the trick:
static readonly IDictionary<Type,Func<object,object[]>> GetItems = new Dictionary<Type,Func<object,object[]>> {
[typeof(ValueTuple<>)] = o => new object[] {((dynamic)o).Item1}
, [typeof(ValueTuple<,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2}
, [typeof(ValueTuple<,,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2, ((dynamic)o).Item3}
, ...
};
This would let you do this:
object[] items = null;
var type = obj.GetType();
if (type.IsGeneric && GetItems.TryGetValue(type.GetGenericTypeDefinition(), out var itemGetter)) {
items = itemGetter(obj);
}
Regarding the part of the question "How can I get each item?"...
Both ValueTuple and Tuple both implement ITuple, which has a length property and an indexer property. So a the following console app code lists the values to the console:
// SUT (as a local function)
IEnumerable<object> GetValuesFromTuple(System.Runtime.CompilerServices.ITuple tuple)
{
for (var i = 0; i < tuple.Length; i++)
yield return tuple[i];
}
// arrange
var valueTuple = (StringProp: "abc", IntProp: 123, BoolProp: false, GuidProp: Guid.Empty);
// act
var values = GetValuesFromTuple(valueTuple);
// assert (to console)
Console.WriteLine($"Values = '{values.Count()}'");
foreach (var value in values)
{
Console.WriteLine($"Value = '{value}'");
}
Console output:
Values = '4'
Value = 'abc'
Value = '123'
Value = 'False'
Value = '00000000-0000-0000-0000-000000000000'
This is my solution to the problem. A PCL compatible extension class. Special thanks to #dasblinkenlight and #Evk for helping me out!
public static class TupleExtensions
{
private static readonly HashSet<Type> ValueTupleTypes = new HashSet<Type>(new Type[]
{
typeof(ValueTuple<>),
typeof(ValueTuple<,>),
typeof(ValueTuple<,,>),
typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>),
typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>),
typeof(ValueTuple<,,,,,,,>)
});
public static bool IsValueTuple(this object obj) => IsValueTupleType(obj.GetType());
public static bool IsValueTupleType(this Type type)
{
return type.GetTypeInfo().IsGenericType && ValueTupleTypes.Contains(type.GetGenericTypeDefinition());
}
public static List<object> GetValueTupleItemObjects(this object tuple) => GetValueTupleItemFields(tuple.GetType()).Select(f => f.GetValue(tuple)).ToList();
public static List<Type> GetValueTupleItemTypes(this Type tupleType) => GetValueTupleItemFields(tupleType).Select(f => f.FieldType).ToList();
public static List<FieldInfo> GetValueTupleItemFields(this Type tupleType)
{
var items = new List<FieldInfo>();
FieldInfo field;
int nth = 1;
while ((field = tupleType.GetRuntimeField($"Item{nth}")) != null)
{
nth++;
items.Add(field);
}
return items;
}
}
hackish one liner
type.Name.StartsWith("ValueTuple`")
(can be extended to check the digit at the end)

Get number of enum values from instance

I'm trying to loop through all of the enums in an object and for each enum, I need to get the number of values for its particular type. So far I have the following code:
var enumProps = testObj.GetType().GetProperties().Where(p => p.PropertyType.IsEnum);
foreach (var prop in enumProps)
{
var propType = prop.GetType();
var valueCount = Enum.GetValues(typeof(propType)).Length; // <-- error
}
The problem is that when I pass propType to typeof, I get the following error:
propType is a variable but used as a type.
Anyone see where I'm going wrong here?
GetType produces the type of prop, which is fixed (i.e. PropertyInfo reflection class). You need to use PropertyType member, like this:
foreach (var prop in enumProps) {
var propType = prop.PropertyType;
var valueCount = Enum.GetValues(propType).Length;
}
Demo.
Note that typeof operator is not necessary, because propType is already a System.Type that you need.

Checking for Nulls on DB Record Mapping

How can I check for db null values in the attached code? Please understand I am a new C# convert...
What this code does is takes a IDataReader object and converts and maps it to a strongly-typed list of objects. But what I am finding is it completely errors out when there are null columns returned in the reader.
Converter
internal class Converter<T> where T : new()
{
// Declare our _converter delegate
readonly Func<IDataReader, T> _converter;
// Declare our internal dataReader
readonly IDataReader dataReader;
// Build our mapping based on the properties in the class/type we've passed in to the class
private Func<IDataReader, T> GetMapFunc()
{
// declare our field count
int _fc = dataReader.FieldCount;
// declare our expression list
List<Expression> exps = new List<Expression>();
// build our parameters for the expression tree
ParameterExpression paramExp = Expression.Parameter(typeof(IDataRecord), "o7thDR");
ParameterExpression targetExp = Expression.Variable(typeof(T));
// Add our expression tree assignment to the exp list
exps.Add(Expression.Assign(targetExp, Expression.New(targetExp.Type)));
//does int based lookup
PropertyInfo indexerInfo = typeof(IDataRecord).GetProperty("Item", new[] { typeof(int) });
// grab a collection of column names from our data reader
var columnNames = Enumerable.Range(0, _fc).Select(i => new { i, name = dataReader.GetName(i) }).AsParallel();
// loop through all our columns and map them properly
foreach (var column in columnNames)
{
// grab our column property
PropertyInfo property = targetExp.Type.GetProperty(column.name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
// check if it's null or not
if (property != null)
{
// build our expression tree to map the column to the T
ConstantExpression columnNameExp = Expression.Constant(column.i);
IndexExpression propertyExp = Expression.MakeIndex(paramExp, indexerInfo, new[] { columnNameExp });
UnaryExpression convertExp = Expression.Convert(propertyExp, property.PropertyType);
BinaryExpression bindExp = Expression.Assign(Expression.Property(targetExp, property), convertExp);
// add it to our expression list
exps.Add(bindExp);
}
}
// add the originating map to our expression list
exps.Add(targetExp);
// return a compiled cached map
return Expression.Lambda<Func<IDataReader, T>>(Expression.Block(new[] { targetExp }, exps), paramExp).Compile();
}
// initialize
internal Converter(IDataReader dataReader)
{
// initialize the internal datareader
this.dataReader = dataReader;
// build our map
_converter = GetMapFunc();
}
// create and map each column to it's respective object
internal T CreateItemFromRow()
{
return _converter(dataReader);
}
}
Mapper
private static IList<T> Map<T>(DbDataReader dr) where T : new()
{
try
{
// initialize our returnable list
List<T> list = new List<T>();
// fire up the lamda mapping
var converter = new Converter<T>(dr);
while (dr.Read())
{
// read in each row, and properly map it to our T object
var obj = converter.CreateItemFromRow();
// add it to our list
list.Add(obj);
}
// reutrn it
return list;
}
catch (Exception ex)
{
// make sure this method returns a default List
return default(List<T>);
}
}
I just don't quite understand where the column to typed object happens in here, so I'd try to do it myself... but I just don;t know where it is.
I know this probably won't help much, but the error I am getting is:
Unable to cast object of type 'System.DBNull' to type 'System.String'.
and it happens on the
internal T CreateItemFromRow()
{
return _converter(dataReader); //<-- Here
}
Note
This does not happen if I wrap the columns in the query itself with an ISNULL(column, ''), but I am sure you can understand that this is surely not a solution
The problem lies in the line convertExp = Expression.Convert(propertyExp, property.PropertyType). You can't expect to convert DbNull value to its equivalent in framework type. This is especially nasty when your type is a value type. One option is to check if the read value from db is DbNull.Value and in case yes, you need to find a compatible value yourself. In some cases people are ok with default values of those types in C#. If you have to do this
property = value == DBNull.Value ? default(T): value;
a generic implementation would look like (as far as the foreach in your converter class goes):
foreach (var column in columns)
{
var property = targetExp.Type.GetProperty(
column.name,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (property == null)
continue;
var columnIndexExp = Expression.Constant(column.i);
var propertyExp = Expression.MakeIndex(
paramExp, indexerInfo, new[] { columnIndexExp });
var convertExp = Expression.Condition(
Expression.Equal(
propertyExp,
Expression.Constant(DBNull.Value)),
Expression.Default(property.PropertyType),
Expression.Convert(propertyExp, property.PropertyType));
var bindExp = Expression.Assign(
Expression.Property(targetExp, property), convertExp);
exps.Add(bindExp);
}
Now this does an equivalent of
property = reader[index] == DBNull.Value ? default(T): reader[index];
You could avoid the double lookup of the reader by assigning it to a variable and using its value in the conditional check. So this should be marginally better, but a lil' more complex:
foreach (var column in columns)
{
var property = targetExp.Type.GetProperty(
column.name,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (property == null)
continue;
var columnIndexExp = Expression.Constant(column.i);
var cellExp = Expression.MakeIndex(
paramExp, indexerInfo, new[] { columnIndexExp });
var cellValueExp = Expression.Variable(typeof(object), "o7thPropValue");
var convertExp = Expression.Condition(
Expression.Equal(
cellValueExp,
Expression.Constant(DBNull.Value)),
Expression.Default(property.PropertyType),
Expression.Convert(cellValueExp, property.PropertyType));
var cellValueReadExp = Expression.Block(new[] { cellValueExp },
Expression.Assign(cellValueExp, cellExp), convertExp);
var bindExp = Expression.Assign(
Expression.Property(targetExp, property), cellValueReadExp);
exps.Add(bindExp);
}
This does the conditional check this way:
value = reader[index];
property = value == DBNull.Value ? default(T): value;
This is one of the most annoying problems in dealing with datasets in general.
The way I normally get around it is to convert the DBNull value to something more useful, like an actual null or even a blank string in some cases. This can be done in a number of ways, but just recently I've taken to using extension methods.
public static T? GetValueOrNull<T>(this object value) where T : struct
{
return value == null || value == DBNull.Value ? (T?) null : (T) Convert.ChangeType(value, typeof (T));
}
A handy extension method for nullable types, so for example:
int? myInt = DataSet.Tables[0].Rows[0]["DBNullInt"].GetValueOrNull<int>();
Or a more generic one to just convert a DBNull in to a null:
public static object GetValueOrNull(this object value)
{
return value == DBNull.Value ? null : value;
}
string myString DataSet.Tables[0].Rows[0]["DBNullString"].GetValueOrNull();
You'll then get a null string, rather than trying to put a DBNull in to a string.
Hopefully that may help you a little.
As I come across this problem recently
both
Expression.TypeIs(propertyExp,typeof(DBNull));
and
Expression.Equal(propertyExp,Expression.Constant(DBNull.Value));
didn't work for me as they did increase memory allocation (which is my primary concern in this case)
here is the benchmark for both mapper approach compare to Dapper on 10K rows query.
TypeIs
and
Equal
so to fix this problem it came out that an IDataRecord is able to call "IsDBNull" to check whether the column in current reader is DBNull or not
and can be write as expression like
var isReaderDbNull = Expression.Call(paramExp, "IsDBNull", null, readerIndex);
finally, I end up with this solution
and now the performance is acceptable again.

How can I access an object's property from the PropertyInfo?

When I foreach through an object's properties and end up with a PropertyInfo, how can I access the actual property of the object?
#foreach(var propertyInfo in Model.Entity.GetType().GetProperties()) {
if (propertyInfo.PropertyType != typeof(string) &&
propertyInfo.PropertyType.GetInterface(typeof(IEnumerable<>).Name) != null) {
<div>
#foreach(var item in propertyInfo/*Need Actual Property Here*/) {
#Html.Action("Edit", new { Id = item.Id, typeName = "Item" });
}
</div>;
}
}
Well, you need an instance to call it on - presumably that's Model.Entity in this case. You need the GetValue method. However, it's going to be quite tricky - because you need two things:
You need the value to be iterable by foreach
You need each item to have an Id property.
If you're using C# 4 and .NET 4, you can use dynamic typing to make it a bit simpler:
IEnumerable values = (IEnumerable) propertyInfo.GetValue(Model.Entity, null);
#foreach(dynamic item in values) {
#Html.Action("Edit", new { Id = item.Id, typeName = "Item" });
}
Or even:
dynamic values = propertyInfo.GetValue(Model.Entity, null);
#foreach(dynamic item in values) {
#Html.Action("Edit", new { Id = item.Id, typeName = "Item" });
}
When you say get the "actual property of the object" if you are referring to the value of the property then you can do something like below.
var item = in propertyInfo.GetValue(Model.Entity, null);
However if you don't have the proper type resolved (which should be object in the above example using var for inference) you will not be able to access the Id property of the value without further reflection or a different dynamic method for accessing the data.
Edit: modified the example not to be in the foreach as #Jon Skeet pointed out it wouldn't compile without the cast and this is moreover to demonstrate retrieving a value from PropertyInfo

Categories