I am putting together simple generic comparer for unit testing.
I don't want to use IComparable interface because there is just too much classes that would need to implement it, and it will be only used in Unit tests so performance of reflection is not an issue.
So far I have this:
public IEnumerable<NonEqualProperty> Compare<T>(T first, T second) where T : class
{
var list = new List<NonEqualProperty>();
var type = first.GetType();
var properties = type.GetProperties();
var basicTypes = properties.Where(p => !p.PropertyType.IsClass && !p.PropertyType.IsInterface
|| p.PropertyType == typeof(string));
foreach (var prop in basicTypes)
{
var value1 = prop.GetValue(first, null);
var value2 = prop.GetValue(second, null);
if (value1 != null && value2 != null && value1.Equals(value2) || value1 == null && value2 == null )
continue;
list.Add(new NonEqualProperty(prop.Name, value1, value2));
}
var enumerableTypes =
from prop in properties
from interfaceType in prop.PropertyType.GetInterfaces()
where interfaceType.IsGenericType
let baseInterface = interfaceType.GetGenericTypeDefinition()
where prop.PropertyType != typeof(string) && baseInterface == typeof(IEnumerable<>) || baseInterface == typeof(IEnumerable)
select prop;
foreach (var prop in enumerableTypes)
{
var collection = prop.GetValue(first, null);
}
return list;
}
So comparison of all simple types + string works.
Now I would like to iterate through IEnumerable (it's always enumerable of a class, though it would be great to take care of the case when it's not) on both sides and compare values using recursion. Something like this:
foreach (var prop in enumerableTypes)
{
var typeOfItemsInList= ...;
var collection1 = (IEnumerable<typeOfItemsInList>) prop.GetValue(first, null);
var collection2 = (IEnumerable<typeOfItemsInList>) prop.GetValue(second, null);
for (int i = 0; i < collection1.Count; i++)
{
var item1 = collection1[i];
var item2 = collection2[i];
Compare<typeOfItemsInList>(item1, item2, list);
}
}
How would I achieve that?
Unequal count, or order of items in lists are not taken into account here - I would fix it later.
Something similar to this:
public static IEnumerable<NonEqualProperty> Compare<T>(T first, T second) where T : class {
var list = new List<NonEqualProperty>();
var type = first.GetType();
var properties = type.GetProperties();
var basicTypes = properties.Where(p => !p.PropertyType.IsClass && !p.PropertyType.IsInterface
|| p.PropertyType == typeof(string));
foreach (var prop in basicTypes) {
var value1 = prop.GetValue(first, null);
var value2 = prop.GetValue(second, null);
if (object.Equals(value1, value2))
continue;
list.Add(new NonEqualProperty(prop.Name, value1, value2));
}
var enumerableTypes =
from prop in properties
where prop.PropertyType == typeof(IEnumerable) ||
prop.PropertyType.GetInterfaces().Any(x => x == typeof(IEnumerable))
select prop;
foreach (var prop in enumerableTypes) {
var value1 = (IEnumerable)prop.GetValue(first, null);
var value2 = (IEnumerable)prop.GetValue(second, null);
if (object.Equals(value1, value2))
continue;
if (value1 == null || value2 == null) {
list.Add(new NonEqualProperty(prop.Name, value1, value2));
continue;
}
IEnumerator enu1 = null, enu2 = null;
try {
try {
enu1 = value1.GetEnumerator();
enu2 = value2.GetEnumerator();
int ix = -1;
while (true) {
bool next1 = enu1.MoveNext();
bool next2 = enu2.MoveNext();
ix++;
if (!next1) {
while (next2) {
list.Add(new NonEqualProperty(prop.Name + "_" + ix, "MISSING", enu2.Current));
ix++;
next2 = enu2.MoveNext();
}
break;
}
if (!next2) {
while (next1) {
list.Add(new NonEqualProperty(prop.Name + "_" + ix, enu1.Current, "MISSING"));
ix++;
next1 = enu1.MoveNext();
}
break;
}
if (enu1.Current != null) {
var type1 = enu1.Current.GetType();
if ((type1.IsClass || type1.IsInterface) && type1 != typeof(string)) {
continue;
}
}
if (enu2.Current != null) {
var type2 = enu2.Current.GetType();
if ((type2.IsClass || type2.IsInterface) && type2 != typeof(string)) {
continue;
}
}
if (!object.Equals(enu1.Current, enu2.Current)) {
list.Add(new NonEqualProperty(prop.Name + "_" + ix, enu1.Current, enu2.Current));
}
}
} finally {
var disp2 = enu2 as IDisposable;
if (disp2 != null) {
disp2.Dispose();
}
}
} finally {
var disp1 = enu1 as IDisposable;
if (disp1 != null) {
disp1.Dispose();
}
}
}
return list;
}
I'm using the IEnumerable non-generic interface. Note that I'm doing the type-test on each element.
To not change the NonEqualProperty class I'm saving the ix of the difference directly in the prop.Name (so like SomeCollection_0, SomeCollection_1 if the different elements are 0 and 1). Normally I would add an Index property to NonEqualProperty For the "missing" elements I'm using a "MISSING" string, that is ok if you want to take a look at it with the debugger, but there are better ways to do it (add another property "Missing" to NonEqualProperty for example, or do a
public static readonly object Missing = new object();
and use it for missing values.
There are other ways without using the IEnumerable interface, like a:
private static IEnumerable<NonEqualProperty> CompareCollection<T>(IEnumerable<T> firstColl, IEnumerable<T> secondColl) {
var list = new List<NonEqualProperty>();
// Do the comparison
return list;
}
private static MethodInfo CompareCollectionMethod = typeof(Program).GetMethod("CompareCollection", BindingFlags.Static | BindingFlags.NonPublic);
and then
var value1 = (IEnumerable)prop.GetValue(first, null);
var value2 = (IEnumerable)prop.GetValue(second, null);
if (object.Equals(value1, value2))
continue;
if (prop.PropertyType != typeof(IEnumerable)) {
var ienumt = prop.PropertyType.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)).FirstOrDefault();
if (ienumt != null) {
var t = ienumt.GetGenericArguments(); // T of IEnumerable<T>
if ((t[0].IsClass || t[0].IsInterface) && t[0] != typeof(string)) {
continue;
}
var method = CompareCollectionMethod.MakeGenericMethod(t);
var result = (IEnumerable<NonEqualProperty>)method.Invoke(null, new[] { value1, value2 });
list.AddRange(result);
continue;
}
}
if (value1 == null || value2 == null) {
list.Add(new NonEqualProperty(prop.Name, value1, value2));
continue;
}
// continue with the code for non-generic IEnumerable
IEnumerator enu1 = null, enu2 = null;
Related
I am trying to generate a human readable string for e.g. ICollection<string> or any other generic ICollection<> / IList<> / IEnumerable<>
In order to do this I use amongst others the following routine:
public static string HumanReadableTypeName(this Type aType)
{
if (aType == null)
throw new ArgumentNullException($"{nameof(ReflectionExtensions)}.{nameof(HumanReadableTypeName)}");
var lBaseType = Nullable.GetUnderlyingType(aType);
if (lBaseType != null)
return cNullableHdr + HumanReadableTypeName(lBaseType);
if (aType == typeof(string))
return aType.Name;
if (aType.IsArray)
{
var elementTypeName = HumanReadableTypeName(aType.GetElementType());
var lBuilder = new StringBuilder(elementTypeName);
for (var i = 0; i < aType.GetArrayRank(); i++) // add brackets for each dimension
lBuilder.Append("[]");
return lBuilder.ToString();
}
var listtype = aType.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IList<>)).FirstOrDefault();
if (listtype != null)
{
var itemType = listtype.GetGenericArguments().Single();
var itemTypeName = HumanReadableTypeName(itemType);
return $"IList<{itemTypeName}>";
}
var collectiontype = aType.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)).FirstOrDefault();
if (collectiontype != null)
{
var itemType = collectiontype.GetGenericArguments().Single();
var itemTypeName = HumanReadableTypeName(itemType);
return $"ICollection<{itemTypeName}>";
}
var enumerabletype = aType.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)).FirstOrDefault();
if (enumerabletype != null)
{
var itemType = enumerabletype.GetGenericArguments().Single();
var itemTypeName = HumanReadableTypeName(itemType);
return $"IEnumerable<{itemTypeName}>";
}
return aType.Name;
}
Trouble is that when I pass typeof(ICollection<string>) as an argument I get IEnumerable<string> as an answer.
In the debugger I entered:
typeof(ICollection<string>).GetInterfaces()
with as result a collection of just 2 elements:
+ [0] {Name = "IEnumerable`1" FullName = "System.Collections.Generic.IEnumerable`1[[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]"} System.Type {System.RuntimeType}
+ [1] {Name = "IEnumerable" FullName = "System.Collections.IEnumerable"} System.Type {System.RuntimeType}
So it seems the ICollection<> interface isn't listed!
What confuses me also is that the following statement:
typeof(ICollection<string>).Name
returns:
"ICollection`1"
So the debugger appears to be able to show it is an ICollection of some kind.
Am I overlooking something, is this featured behavior, or is it a bug in VS22 / .NET7
TIA
As Etienne and 4168... reported, I overlooked the fact that an interface definition does not support itself.
So I fixed the method, and now it looks like this, in addition having optional support to look for the supported interfaces, and support for IDictionary<,>
public const string cNullableHdr = "Nullable";
public static string HumanReadableTypeName(this Type aType, bool checkSupportedInterfaces=true)
{
if (aType == null)
throw new ArgumentNullException($"{nameof(ReflectionExtensions)}.{nameof(HumanReadableTypeName)}");
var lBaseType = Nullable.GetUnderlyingType(aType);
if (lBaseType != null)
return cNullableHdr + HumanReadableTypeName(lBaseType);
if (aType == typeof(string))
return aType.Name;
if (aType.IsArray)
{
var elementTypeName = HumanReadableTypeName(aType.GetElementType());
var lBuilder = new StringBuilder(elementTypeName);
for (var i = 0; i < aType.GetArrayRank(); i++) // add brackets for each dimension
lBuilder.Append("[]");
return lBuilder.ToString();
}
// check if the type IS one of the most supported collection types
if (aType.IsGenericType && aType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
var genericargs = aType.GetGenericArguments();
var keyTypeName = HumanReadableTypeName(genericargs[0]);
var valueTypeName = HumanReadableTypeName(genericargs[1]);
return $"IDictionary<{keyTypeName},{valueTypeName}>";
}
if (aType.IsGenericType && aType.GetGenericTypeDefinition() == typeof(IList<>))
{
var itemType = aType.GetGenericArguments().Single();
var itemTypeName = HumanReadableTypeName(itemType);
return $"IList<{itemTypeName}>";
}
if (aType.IsGenericType && aType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
var itemType = aType.GetGenericArguments().Single();
var itemTypeName = HumanReadableTypeName(itemType);
return $"ICollection<{itemTypeName}>";
}
if (aType.IsGenericType && aType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
var itemType = aType.GetGenericArguments().Single();
var itemTypeName = HumanReadableTypeName(itemType);
return $"IEnumerable<{itemTypeName}>";
}
if (checkSupportedInterfaces)
{
var dictionaryType = aType.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>)).FirstOrDefault();
if (dictionaryType != null)
{
var genericargs = dictionaryType.GetGenericArguments();
var keyTypeName = HumanReadableTypeName(genericargs[0]);
var valueTypeName = HumanReadableTypeName(genericargs[1]);
return $"IDictionary<{keyTypeName},{valueTypeName}>";
}
// check if the type SUPPORTS one of the most supported collection types
var listtype = aType.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IList<>)).FirstOrDefault();
if (listtype != null)
{
var itemType = listtype.GetGenericArguments().Single();
var itemTypeName = HumanReadableTypeName(itemType);
return $"IList<{itemTypeName}>";
}
var collectiontype = aType.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)).FirstOrDefault();
if (collectiontype != null)
{
var itemType = collectiontype.GetGenericArguments().Single();
var itemTypeName = HumanReadableTypeName(itemType);
return $"ICollection<{itemTypeName}>";
}
var enumerabletype = aType.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)).FirstOrDefault();
if (enumerabletype != null)
{
var itemType = enumerabletype.GetGenericArguments().Single();
var itemTypeName = HumanReadableTypeName(itemType);
return $"IEnumerable<{itemTypeName}>";
}
}
// otherwise, just return the system type name
return aType.Name;
}
i started with this! and i would like to avoid this
private IQueryable<Customer> FilterResult(string search, List<Customer> dtResult, List<string> columnFilters)
{
IQueryable<Customer> results = dtResult.AsQueryable();
results = results.Where(p => (search == null || (p.Name != null && p.Name.ToLower().Contains(search.ToLower()) || p.City != null && p.City.ToLower().Contains(search.ToLower())
&& (columnFilters[0] == null || (p.Name != null && p.Name.ToLower().Contains(columnFilters[0].ToLower())))
&& (columnFilters[1] == null || (p.City != null && p.City.ToLower().Contains(columnFilters[1].ToLower())))
);
return results;
}
by using reflection maybe.... because imagine i have 100 properties it would be easy to mess up ...so i tried this way i would like to use reflection to loop on all properties instead of making references to each of them(properties)
public IQueryable<Aangifte> FilterColumn<T>()
{
try
{
List<Aangifte> v = AangifteGetData.GetData(StartDate, EndDate);
List<Aangifte> v2 = new List<Aangifte>();
Aangifte Foo = new Aangifte();
List<string> propertEntity = new List<string>();
var search = Request.Form.GetValues("search[value]").FirstOrDefault();
int count = -1;
var results = v.AsQueryable();
List<string> columnName = new List<string>();
foreach (var prop in Foo.GetType().GetProperties())
{
var t = "";
if (!prop.Name.Contains("FORMAT"))
{
TVALUE = prop.GetValue(Foo, null);
t= prop.Name;
propertEntity.Add(t);
count++;
}
if (count < propertEntity.Count())
{
var newt = t;
var Z = Request.Form.GetValues("columns[" + count + "][search][value]").FirstOrDefault();
results = results.Where
(
p => (search == null || (t != null && t.ToLower().Contains(search.ToLower())))
&& (Request.Form.GetValues("columns[" + count + "][search][value]").FirstOrDefault() == null || (t != null && t.ToLower().Contains(Request.Form.GetValues("columns[" + count + "][search][value]").FirstOrDefault().ToLower())))
);
}
}
return results;
}
catch (Exception EX)
{
throw EX;
}
}
I will explain my Problem
so Firstly, I use Predicates Linq for Build Where clause dynamically.
I have to build dynamically because I don't know how many parameters will come. Let me give an example. For the A column can be one parameters however, for the B column can be 2 parameters like either value 'Gas' or 'Oil' which select but that's big problem I can not combine for these 2 column correctly.
So as a result, this code work but It return 0 Items. But there are I know.
public List<CarEntity> GetSearchByKCriteria(int cityId, List<string> fuelType, List<string> gearType, List<string> budget,
List<string> caroser, List<string> enginePower)
{
Expression<Func<Car, bool>> query = null;
Expression<Func<Car, bool>> combine = null;
foreach (var bud in budget)
{
if (budget.Count >= 1)
{
if (bud == "1")
{
if (budget.Count > 1)
{
query = car => car.Budget >= 20000 && car.Budget <= 34999;
}
else
{
query = car => car.Budget >= 20000 && car.Budget <= 34999;
}
}
else if (bud == "2")
{
if (query != null)
{
combine = car => (car.Budget >= 35000 && car.Budget <= 49999);
query = query.Or(combine);
}
else
{
query = car => car.Budget >= 35000 && car.Budget <= 49999;
}
}
}
}
foreach (var caros in caroser)
{
if (caros != "-1" && !string.IsNullOrEmpty(caros))
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("karoser"))
{
combine = car => (car.Karoser == caros);
query = query.And(combine);
}
else
{
combine = car => car.Karoser == caros;
query = query.And(combine);
}
}
else
{
query = car => car.Karoser == caros;
}
}
}
foreach (var fuel in fuelType)
{
if (fuel != "-1" && !string.IsNullOrEmpty(fuel))
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("yakituru"))
{
combine = car => (car.YakitTuru==fuel);
query = query.Or(combine);
}
else
{
combine = car => car.YakitTuru == fuel;
query = query.And(combine);
}
}
else
{
query = car => car.YakitTuru == fuel;
}
}
}
foreach (var gear in gearType)
{
if (gear!="-1"&& !string.IsNullOrEmpty(gear))
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("sanzimantipi"))
{
combine = car => (car.SanzimanTipi == gear);
query = query.Or(combine);
}
else
{
combine = car => car.SanzimanTipi == gear;
query = query.And(combine);
}
}
else
{
query = car => car.SanzimanTipi == gear;
}
}
}
foreach (var engine in enginePower)
{
if (enginePower.Count >= 1)
{
if (engine == "1")
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("silindirhacmi"))
{
combine = car => (car.SilindirHacmi >= 0 && car.SilindirHacmi <= 1600);
query = query.Or(combine);
}
else
{
combine = car => (car.SilindirHacmi >= 0 && car.SilindirHacmi <= 1600);
query = query.And(combine);
}
}
else
{
query = car => car.SilindirHacmi >= 0 && car.SilindirHacmi <= 1600;
}
}
if (engine == "3")
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("silindirhacmi"))
{
combine = car => (car.SilindirHacmi >= 1601 && car.SilindirHacmi <= 1800);
query = query.Or(combine);
}
else
{
combine = car => (car.SilindirHacmi >= 1601 && car.SilindirHacmi <= 1800);
query = query.And(combine);
}
}
else
{
query = car => car.SilindirHacmi >= 1601 && car.SilindirHacmi <= 1800;
}
}
}
using (var context = DataContextFactory.CreateContext())
{
var result = (from fkCar in context.Car.Where(query)
join pkCarBrand in context.CarBrand on fkCar.Marka equals pkCarBrand.ID
where fkCar.IsActive == true
select new
{
entity = fkCar,
joinEntity = pkCarBrand
});
List<CarEntity> theCarList = new List<CarEntity>();
foreach (var item in result)
{
CarEntity theEntity = Mapper.Map(item.entity);
theEntity.CarBrand = Mapper.Map(item.joinEntity);
theCarList.Add(theEntity);
}
return theCarList;
}
}
So thanks for reply,
I faced a similar challenge a while back, where I wanted to have a list of allowed values for an attribute where, if matched, the associated instance would pass the filter. I came up with the following extension method:
static public Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values) { throw new ArgumentNullException("values"); }
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
{
return e => false;
}
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
This is based on the discussion and code posted at http://www.velocityreviews.com/forums/t645784-linq-where-clause.html
I am trying to write a generic method to compare two objects (I purposefully have two different types coming in. The second one has the same properties as the first one. The first just has more.).
I want to make sure the properties have the same values. The following code works for most of the properties that I have in the objects, but occasionally it throws the:
"Object Does Not Match Target Type"
...error at
var valFirst = prop.GetValue(manuallyCreated, null) as IComparable;
public static bool SameCompare<T, T2>(T manuallyCreated, T2 generated){
var propertiesForGenerated = generated.GetType().GetProperties();
int compareValue = 0;
foreach (var prop in propertiesForGenerated) {
var valFirst = prop.GetValue(manuallyCreated, null) as IComparable;
var valSecond = prop.GetValue(generated, null) as IComparable;
if (valFirst == null && valSecond == null)
continue;
else if (valFirst == null) {
System.Diagnostics.Debug.WriteLine(prop + "s are not equal");
return false;
}
else if (valSecond == null) {
System.Diagnostics.Debug.WriteLine(prop + "s are not equal");
return false;
}
else if (prop.PropertyType == typeof(System.DateTime)) {
TimeSpan timeDiff = (DateTime)valFirst - (DateTime)valSecond;
if (timeDiff.Seconds != 0) {
System.Diagnostics.Debug.WriteLine(prop + "s are not equal");
return false;
}
}
else
compareValue = valFirst.CompareTo(valSecond);
if (compareValue != 0) {
System.Diagnostics.Debug.WriteLine(prop + "s are not equal");
return false;
}
}
System.Diagnostics.Debug.WriteLine("All values are equal");
return true;
}
Each property defined on a type in .NET is different, even if they have the same name. You'd have to do this:
foreach (var prop in propertiesForGenerated)
{
var otherProp = typeof(T).GetProperty(prop.Name);
var valFirst = otherProp.GetValue(manuallyCreated, null) as IComparable;
var valSecond = prop.GetValue(generated, null) as IComparable;
...
Of course this doesn't take into account that some properties defined on T may not exist on T2, and vice versa.
Because manuallyCreated is not of type T2. Try getting the property object with the same name on that type, and it should work (or if your assumption that all properties in T2 are also in T1, you'll get a better error):
public static bool SameCompare<T, T2>(T manuallyCreated, T2 generated){
var propertiesForGenerated = typeof(T2).GetProperties();
int compareValue = 0;
foreach (var prop in propertiesForGenerated) {
var firstProperty = typeof(T).GetProperty(prop.Name);
var valFirst = firstProperty.GetValue(manuallyCreated, null) as IComparable;
var valSecond = prop.GetValue(generated, null) as IComparable;
...
}
}
I believe it would be better to have Interface of the properties you need to compare,
I would then inherit IEqualityComparer<YourInterface>
and in Equals and GetHashCode
public bool Equals(YourInterface one, YourInterface two)
{
return this.GetHashCode(one) == this.GetHashCode(two);
}
public int GetHashCode(YourInterface test)
{
if(test == null)
{
return 0;
}
int hash = 0;
//// get hash or set int on each property.
return hash;
}
this old code returns a list of fields decorated with an attribute in a method call using reflection
Is there a way to replace it with TypeDescripter or LINQ ?
public static FieldInfo[] GetFieldsWithAttribute(Type type, Attribute attr, bool onlyFromType)
{
System.Reflection.FieldInfo[] infos = type.GetFields();
int cnt = 0;
foreach (System.Reflection.FieldInfo info in infos)
{
if (info.GetCustomAttributes(attr.GetType(), false).Length > 0)
{
if (onlyFromType && info.DeclaringType != type)
continue;
cnt++;
}
}
System.Reflection.FieldInfo[] rc = new System.Reflection.FieldInfo[cnt];
// now reset !
cnt = 0;
foreach (System.Reflection.FieldInfo info in infos)
{
if (info.GetCustomAttributes(attr.GetType(), false).Length > 0)
{
if (onlyFromType && info.DeclaringType != type)
continue;
rc[cnt++] = info;
}
}
return rc;
}
public static FieldInfo[] GetFieldsWithAttribute(Type type, Attribute attr, bool onlyFromType)
{
System.Reflection.FieldInfo[] infos = type.GetFields();
var selection =
infos.Where(info =>
(info.GetCustomAttributes(attr.GetType(), false).Length > 0) &&
((!onlyFromType) || (info.DeclaringType == type)));
return selection.ToArray();
}
If you can return an IEnumerable<FieldInfo>, you should be able to return selection directly.
How about:
return type
.GetFields()
.Where(fi =>
fi.GetCustomAttributes(attr.GetType(), false).Length > 0
&& !(onlyFromType && fi.DeclaringType != type))
.ToArray();