Checking if a generic IEnumerable is empty - c#

Let's say I have an object which may be of type IEnumerable<T>. I want to write a method that returns true if the object is of type IEnumerable<T>, is not null, and is not empty.
Here's what I've got so far:
public bool IsNullOrEmpty(object obj)
{
if (obj != null)
{
if (obj is IEnumerable<object>)
{
return (obj as IEnumerable<object>).Any();
}
}
return false;
}
This works if I pass in an object that is of type List<string>, but not if I pass in an object that is of type List<int>. It fails because because obj is IEnumerable<object> returns false.
Any idea how I can make this work for all generic IEnumerables?

Since the type may be unknown, you can try check for IEnumerable interface and use MoveNext() on the enumerator.
EDIT: I updated the method name. It makes more sense with the logic now since the original question code was checking if there were items in the collection.
public bool IsNotNullOrEmpty(object enumerable)
{
if (enumerable != null)
{
if (enumerable is IEnumerable)
{
using(var enumerator = ((IEnumerable)enumerable).GetEnumerator())
return enumerator.MoveNext();
}
}
return false;
}

System.Collections.Generic.IEnumerable<T> inherits from System.Collections.IEnumerable - thus, if you are ok with checking the non-generic IEnumerable, rather than the generic IEnumerable<T>, you could just cast to IEnumerable.
A few notes about your code: You are first checking with is, and then you cast with as. That is generally unnecessary; as already checks and returns null if the cast failed. Therefore, a shorter way would be:
var enumerable = obj as IEnumerable;
if (enumerable != null) {
return !enumerable.Cast<object>().Any();
}
Note that you will need the additional call to Cast there, as Any requires a generic IEnumerable<T>.

You can try to cast it to IEnumerable:
public static bool IsNullOrEmpty<T>(this T obj) where T : class
{
if (obj == null) return true;
IEnumerable seq = obj as IEnumerable;
if (seq != null) return !seq.Cast<object>().Any();
return false;
}
...
List<int> list = new List<int>();
bool nullOrEmpty = list.IsNullOrEmpty(); // true
Btw, interestingly enough it works correctly with an empty string:
bool nullOrEmpty = "".IsNullOrEmpty(); // true

You can check for the non-generic IEnumerable and check that for emptiness. You can add a check to ensure the object implements IEnumerable<T> using reflection:
public static bool IsNullOrEmpty(object obj)
{
var e = obj as System.Collections.IEnumerable;
if (e == null || !e.GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))) return false;
foreach (object _ in e)
{
return true;
}
return false;
}

For completeness, adding the state-of-the-art answers for the recent c# versions (from c#8.0):
static bool IsNotNullOrEmpty<T>(object obj) => obj is IEnumerable<T> seq && seq.Any();
static bool IsNullOrEmpty<T>(object obj) => !(obj is IEnumerable<T> seq && seq.Any());
This assumes you will know T in the calling site.
If you won't know T, you shouldn't be using IEnumerable<object>, but IEnumerable instead. Something like this:
static bool IsNotNullOrEmpty(object obj) => obj is IEnumerable seq && seq.GetEnumerator().MoveNext();

You can first check if the object implements ICollection like lists and arrays do, as checking the size of one of those is cheaper as they have a Count property. If it's not you can check if it implements IEnumerable and if it does, create an enumerator and see if you can move to the first item:
public static bool IsNullOrEmpty(object obj) {
ICollection c = obj as ICollection;
if (c != null) {
return c.Count == 0;
}
IEnumerable e = obj as IEnumerable;
if (e != null) {
return !e.GetEnumerator().MoveNext();
}
return false;
}

This answers expands the question outside of the IEnumerable<T> scope to "can this thing I have be used in a foreach-loop?".
As you might known, foreach does not check for IEnumerable primarily, but rather tries to enumerate whatever is given.
This will work for all types which in some sense is enumerable in the sense that foreach thinks:
public bool IsNullOrEmpty(object obj)
{
if (obj != null) // we can't be null
{
var method = obj.GetType().GetMethod("GetEnumerator");
if (method != null) // we have an enumerator method
{
var enumerator = method.Invoke(obj, null);
if (enumerator != null) // we got the enumerator
{
var moveMethod = enumerator.GetType().GetMethod("MoveNext")
if (moveMethod != null) // we have a movenext method, lets try it
return !(bool)moveMethod.Invoke(enumerator, null); // ie true = empty, false = has elements
}
}
}
return true; // it's empty, lets return true
}
This basically does the same as the foreach-loop, ie checks for enumerability and doesn't really give a darn about the types involved.
As such it should really be avoided, but if you somehow have to check if you can put your type in foreach-loop this should do the trick.

Related

Compare generic two objects

I'm trying to do a simple comparison on two objects, simply to track changes on saving to the database, I have the below code which works perfectly on simple objects.
public static List<Variance> Compare<T>(this T original, T current)
{
var variances = new List<Variance>();
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var v = new Variance
{
PropertyName = property.Name,
Original = property.GetValue(original),
Current = property.GetValue(current)
};
if (v.Original == null && v.Current == null)
{
continue;
}
if ((v.Original == null && v.Current != null) || (v.Original != null && v.Current == null))
{
variances.Add(v);
continue;
}
if (!v.Original.Equals(v.Current))
{
variances.Add(v);
}
}
return variances;
}
but if T is a list the equals need to be changed to a SequenceEqual and I can't think of a way to convert T to the correct list type to do the SequenceEqual check.
Although it's not impossible to use SequenceEqual here, it'd involve either dynamic like in Jasper Kent's answer, or quite a lot of reflection code, I'll outline what you'll need to do if you were using reflection:
check if both the objects are IEnumerable<T>.
get the Type of both objects, and then get the type parameter T as a Type as well
get the method SequenceEquals from Enumerable as a MethodInfo. This involves using LINQ to find the overload with two parameters.
call MakeGenericMethod with T.
Invoke the MethodInfo
I wouldn't want to read, or write that code... Using dynamic is fine I guess, though some people have different opinions...
So here's a third way: I suggest you write your own SequenceEqual method that takes IEnumerables (the non-generic version):
private static bool SequenceEqual(IEnumerable first, IEnumerable second) {
IEnumerator e1 = first.GetEnumerator();
IEnumerator e2 = second.GetEnumerator();
try {
// adapted from https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,63644a881e976b52,references
while (e1.MoveNext())
{
if (!(e2.MoveNext() && e1.Current.Equals(e2.Current))) return false;
}
if (e2.MoveNext()) return false;
return true;
} finally {
if (e1 is IDisposable d1) {
d1.Dispose();
}
if (e2 is IDisposable d2) {
d2.Dispose();
}
}
}
Then you can just check whether the objects are IEnumerable:
if (v.Original is Enumerable e1 &&
v.Current is Enumerable e2 &&
!SequenceEqual(e1, e2))
{
variances.Add(v);
}
I think the solution you want is to use dynamic overloading.
I've cut this solution down from yours to get to the nub of it. The call to SwitchEquals() is the equivalent of your v.Original.Equals(v.Current).
static bool SwitchEquals<T>(IEnumerable<T> listA, IEnumerable<T> listB)
{
Console.WriteLine("Doing Sequential Equals");
return true; // Do your sequential equal here
}
static bool SwitchEquals(object objA, object objB)
{
Console.WriteLine("Doing equals");
return objA.Equals(objB); // This is your original equals
}
static void Compare<T>(T original, T current)
{
// Using dynamic means the decision between the tow SwitchEquals methods is made
// At runtime, when the system knows if we have a collection
if (SwitchEquals ((dynamic)original,(dynamic) current))
Console.WriteLine("Match");
else
Console.WriteLine("No match");
}
static void Main(string[] args)
{
Compare(4,5);
Compare (new int[] { 4, 3 }, new int[] { 4, 4 });
}

How do I test if an IHttpActionResult returns OkNegotiatedContentType<anonymous>? [duplicate]

I would like to perform a test if an object is of a generic type. I've tried the following without success:
public bool Test()
{
List<int> list = new List<int>();
return list.GetType() == typeof(List<>);
}
What am I doing wrong and how do I perform this test?
If you want to check if it's an instance of a generic type:
return list.GetType().IsGenericType;
If you want to check if it's a generic List<T>:
return list.GetType().GetGenericTypeDefinition() == typeof(List<>);
As Jon points out, this checks the exact type equivalence. Returning false doesn't necessarily mean list is List<T> returns false (i.e. the object cannot be assigned to a List<T> variable).
I assume that you don't just want to know if the type is generic, but if an object is an instance of a particular generic type, without knowing the type arguments.
It's not terribly simple, unfortunately. It's not too bad if the generic type is a class (as it is in this case) but it's harder for interfaces. Here's the code for a class:
using System;
using System.Collections.Generic;
using System.Reflection;
class Test
{
static bool IsInstanceOfGenericType(Type genericType, object instance)
{
Type type = instance.GetType();
while (type != null)
{
if (type.IsGenericType &&
type.GetGenericTypeDefinition() == genericType)
{
return true;
}
type = type.BaseType;
}
return false;
}
static void Main(string[] args)
{
// True
Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
new List<string>()));
// False
Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
new string[0]));
// True
Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
new SubList()));
// True
Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
new SubList<int>()));
}
class SubList : List<string>
{
}
class SubList<T> : List<T>
{
}
}
EDIT: As noted in comments, this may work for interfaces:
foreach (var i in type.GetInterfaces())
{
if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType)
{
return true;
}
}
I have a sneaking suspicion there may be some awkward edge cases around this, but I can't find one it fails for right now.
These are my two favorite extension methods that cover most edge cases of generic type checking:
Works with:
Multiple (generic) interfaces
Multiple (generic) base classes
Has an overload that will 'out' the specific generic type if it returns true (see unit test for samples):
public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
{
Type concreteType;
return typeToCheck.IsOfGenericType(genericType, out concreteType);
}
public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType)
{
while (true)
{
concreteGenericType = null;
if (genericType == null)
throw new ArgumentNullException(nameof(genericType));
if (!genericType.IsGenericTypeDefinition)
throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
if (typeToCheck == null || typeToCheck == typeof(object))
return false;
if (typeToCheck == genericType)
{
concreteGenericType = typeToCheck;
return true;
}
if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
{
concreteGenericType = typeToCheck;
return true;
}
if (genericType.IsInterface)
foreach (var i in typeToCheck.GetInterfaces())
if (i.IsOfGenericType(genericType, out concreteGenericType))
return true;
typeToCheck = typeToCheck.BaseType;
}
}
Here's a test to demonstrate the (basic) functionality:
[Test]
public void SimpleGenericInterfaces()
{
Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>)));
Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>)));
Type concreteType;
Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType));
Assert.AreEqual(typeof(IEnumerable<string>), concreteType);
Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType));
Assert.AreEqual(typeof(IQueryable<string>), concreteType);
}
You can use shorter code using dynamic althougth this may be slower than pure reflection:
public static class Extension
{
public static bool IsGenericList(this object o)
{
return IsGeneric((dynamic)o);
}
public static bool IsGeneric<T>(List<T> o)
{
return true;
}
public static bool IsGeneric( object o)
{
return false;
}
}
var l = new List<int>();
l.IsGenericList().Should().BeTrue();
var o = new object();
o.IsGenericList().Should().BeFalse();
return list.GetType().IsGenericType;
public static string WhatIsMyType<T>()
{
return typeof(T).NameWithGenerics();
}
public static string NameWithGenerics(this Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (type.IsArray)
return $"{type.GetElementType()?.Name}[]";
if (!type.IsGenericType)
return type.Name;
var name = type.GetGenericTypeDefinition().Name;
var index = name.IndexOf('`');
var newName = index == -1 ? name : name.Substring(0, index);
var list = type.GetGenericArguments().Select(NameWithGenerics).ToList();
return $"{newName}<{string.Join(",", list)}>";
}
Now test with this:
Console.WriteLine(WhatIsMyType<IEnumerable<string>>());
Console.WriteLine(WhatIsMyType<List<int>>());
Console.WriteLine(WhatIsMyType<IList<int>>());
Console.WriteLine(WhatIsMyType<List<ContentBlob>>());
Console.WriteLine(WhatIsMyType<int[]>());
Console.WriteLine(WhatIsMyType<ContentBlob>());
Console.WriteLine(WhatIsMyType<Dictionary<string, Dictionary<int, int>>>());
and you will get
IEnumerable<String>
List<Int32>
IList<Int32>
List<ContentBlob>
Int32[]
ContentBlob
Dictionary<String,Dictionary<Int32,Int32>>

How do I specify a constraint that says "collection of nullable types"?

I'm trying to create a generic argument-validation method that checks collection parameters for null, empty, or contains a null element.
public void Foo(ICollection<MyType> bar)
{
// Validate parameters
ThrowIfNullEmptyOrContainsNull(bar, "bar");
.
.
.
If I only specify ICollection<T> in the type constraint, then if (value.Contains(null)) generates an error, since T may not be a nullable type.
This was what I came up with, but it doesn't seem to be right:
internal static T1 ThrowIfNullEmptyOrContainsNull<T1, T2>(T1 value, string name)
where T1 : ICollection<T2>
where T2 : class
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException(name);
if (value.Count == 0)
throw new ArgumentException("Empty collection not allowed", name);
if (value.Contains(null))
throw new ArgumentException("Collection contains one or more null elements", name);
return value;
}
...but then I have to call the method with explicit argument types, something like this:
public void Foo(ICollection<MyType> bar)
{
// Validate parameters
ThrowIfNullEmptyOrContainsNull<(ICollection<MyType>, MyType>(bar, "bar");
.
.
.
Without explicitly specifying T1 and T2 in the call, I get an error "The type arguments ... cannot be inferred from the usage".
Can anyone shed light on how to do this?
Just don't use Contains. Iterate through the collection and compare the values to null explicitly:
internal static T1 ThrowIfNullEmptyOrContainsNull<T1, T2>(T1 value, string name)
where T1 : ICollection<T2>
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException(name);
if (value.Count == 0)
throw new ArgumentException("Empty collection not allowed", name);
foreach (var item in value)
if (item == null)
throw new ArgumentException("Collection contains one or more null elements", name);
return value;
}
What about something like:
if (value.Any(item => item == null))
{
throw new ArgumentException("Collection contains one or more null elements", name);
}
As in:
internal static T1 ThrowIfNullEmptyOrContainsNull<T1, T2>(T1 value, string name)
where T1 : ICollection<T2>
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException(name);
if (value.Count == 0)
throw new ArgumentException("Empty collection not allowed", name);
if (value.Any(item => item == null))
throw new ArgumentException("Collection contains 1 or more null items", name);
return value;
}
We can compare a non-nullable type with null, because all objects can be cast to object and hence compared:
bool obviouslyFalse = 1 == null;
This will result in a warning, but is valid. Obviously it's going to always be false and indeed the compiler will optimise by removing the comparison and giving us the equivalent as if we had bool obviouslyFalse = false;.
With generics the same applies in that with:
T item = getTFromSomewhere;
bool obviouslyFalseIfTIsntNullable = item == null;
Then this is valid for all possible T, and while the compiler can't remove the comparison, the jitter can, and indeed will.
Hence therefore we can have:
internal static TCol ThrowIfNullEmptyOrContainsNull<TCol, TEl>(TCol collection, string name)
where TCol : ICollection<TEl>
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException(name);
if (value.Count == 0)
throw new ArgumentException("Empty collection not allowed", name);
foreach(var item in collection)
if(item == null)
throw new ArgumentException("Collection cannot contain null elements", name);
return value;
}
This will work, but is wasteful if we've a large collection of non-nullable types; the jitter is not likely to remove that iteration, even if it does nothing, so it'll still get an enumerator and call MoveNext() on it until it returns false. We can help it:
internal static TCol ThrowIfNullEmptyOrContainsNull<TCol, TEl>(TCol collection, string name)
where TCol : ICollection<TEl>
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException(name);
if (value.Count == 0)
throw new ArgumentException("Empty collection not allowed", name);
if(default(TEl) == null)
foreach(var item in collection)
if(item == null)
throw new ArgumentException("Collection cannot contain null elements", name);
return value;
}
Because default(TEl) == null is always true for nullable types (including Nullable<T>) and always false for non-nullable types, the jitter will optimise by cutting out this comparison for all types, and removing the entire enumeration for non-nullable types. Hence a massive array of integers (for example) will be okayed by the method immediately.
What about an extension method. Wrap with exceptions or messages as required
public static bool IsNullOrEmpty<T>(this ICollection<T> alist) where T:class
{
if (alist == null || alist.Count == 0){
return true;
}
if (alist.Any(t => t == null)) {
return true;
}
return false;
}
use:
if ( myList.IsNullOrEmpty ) {
//.. exception, error handling
}
No need to pass the type of MyList since MyList must implement ICollection
Might be useful for you no need to pass type requirement.
Added T:Class to suggestion. But you knew about that already :-)

Check to see if a given object (reference or value type) is equal to its default

I'm trying to find a way to check and see if the value of a given object is equal to its default value. I've looked around and come up with this:
public static bool IsNullOrDefault<T>(T argument)
{
if (argument is ValueType || argument != null)
{
return object.Equals(argument, default(T));
}
return true;
}
The problem I'm having is that I want to call it like this:
object o = 0;
bool b = Utility.Utility.IsNullOrDefault(o);
Yes o is an object, but I want to make it figure out the base type and check the default value of that. The base type, in this case, is an integer and I want to know in this case if the value is equal to default(int), not default(object).
I'm starting to think this might not be possible.
In your example, your integer is boxed and therefore your T is going to be object, and the default of object is null, so that's not valuable to you. If the object is a value type, you could get an instance of it (which would be the default) to use as a comparison. Something like:
if (argument is ValueType)
{
object obj = Activator.CreateInstance(argument.GetType());
return obj.Equals(argument);
}
You'd want to deal with other possibilities before resorting to this. Marc Gravell's answer brings up some good points to consider, but for a full version of your method, you might have
public static bool IsNullOrDefault<T>(T argument)
{
// deal with normal scenarios
if (argument == null) return true;
if (object.Equals(argument, default(T))) return true;
// deal with non-null nullables
Type methodType = typeof(T);
if (Nullable.GetUnderlyingType(methodType) != null) return false;
// deal with boxed value types
Type argumentType = argument.GetType();
if (argumentType.IsValueType && argumentType != methodType)
{
object obj = Activator.CreateInstance(argument.GetType());
return obj.Equals(argument);
}
return false;
}
if o is null, in a non-generic (object) method, you will have no access to the original type - and you can't do much about that.
Hence, the only time it matters is non-nullable value-types, so:
Type type = value.GetType();
if(!type.IsValueType) return false; // can't be, as would be null
if(Nullable.GetUnderlyingType(type) != null) return false; // ditto, Nullable<T>
object defaultValue = Activator.CreateInstance(type); // must exist for structs
return value.Equals(defaultValue);
Converted Anthony Pegram's Answer into an extension method:
using System;
//Adapted from https://stackoverflow.com/a/6553276/1889720
public static class ObjectExtensions
{
public static bool IsNullOrDefault<TObject>(this TObject argument)
{
// deal with normal scenarios
if (argument == null)
{
return true;
}
if (object.Equals(argument, default(TObject)))
{
return true;
}
// deal with non-null nullables
Type methodType = typeof(TObject);
if (Nullable.GetUnderlyingType(methodType) != null)
{
return false;
}
// deal with boxed value types
Type argumentType = argument.GetType();
if (argumentType.IsValueType && argumentType != methodType)
{
object obj = Activator.CreateInstance(argument.GetType());
return obj.Equals(argument);
}
return false;
}
}
Usage syntax:
myVariable.IsNullOrDefault();
Expanding on Marc Gravell's answer, by getting the run-time type as an argument:
// Handles boxed value types
public static bool IsNullOrDefault([CanBeNull] this object #object,
[NotNull] Type runtimeType)
{
if (#object == null) return true;
if (runtimeType == null) throw new ArgumentNullException("runtimeType");
// Handle non-null reference types.
if (!runtimeType.IsValueType) return false;
// Nullable, but not null
if (Nullable.GetUnderlyingType(runtimeType) != null) return false;
// Use CreateInstance as the most reliable way to get default value for a value type
object defaultValue = Activator.CreateInstance(runtimeType);
return defaultValue.Equals(#object);
}
For those of you that will challenge my use case, I want to list the values of properties on an arbitrary object, omitting properties which set to their defaults (for a more concise display).
Because propertyInfo.GetValue(targetObject, null) returns an object, and value types are boxed, I cannot use a generic method. By passing propertyInfo.PropertyType as the second parameter to this method I can avoid the problem a generic method has with boxed value types.
The following will sort it out.
public static bool IsNullOrDefault<T>(T argument)
{
if (argument is ValueType || argument != null)
{
return object.Equals(argument, GetDefault(argument.GetType()));
}
return true;
}
public static object GetDefault(Type type)
{
if(type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
Make an extension method
public static class DateExtension
{
public static bool IsNullOrDefault(this DateTime? value)
{
return default(DateTime) == value || default(DateTime?) == value;
}
}
Solution with linq expressions. First call for type will be relatively slow, but then it should work just as quick as usual code.
public static class DefaultHelper
{
private delegate bool IsDefaultValueDelegate(object value);
private static readonly ConcurrentDictionary<Type, IsDefaultValueDelegate> Delegates
= new ConcurrentDictionary<Type, IsDefaultValueDelegate>();
public static bool IsDefaultValue(this object value)
{
var type = value.GetType();
var isDefaultDelegate = Delegates.GetOrAdd(type, CreateDelegate);
return isDefaultDelegate(value);
}
private static IsDefaultValueDelegate CreateDelegate(Type type)
{
var parameter = Expression.Parameter(typeof(object));
var expression = Expression.Equal(
Expression.Convert(parameter, type),
Expression.Default(type));
return Expression.Lambda<IsDefaultValueDelegate>(expression, parameter).Compile();
}
}

IEnumerable<T> and reflection

Background
Working in .NET 2.0 Here, reflecting lists in general. I was originally using t.IsAssignableFrom(typeof(IEnumerable)) to detect if a Property I was traversing supported the IEnumerable Interface. (And thus I could cast the object to it safely)
However this code was not evaluating to True when the object is a BindingList<T>.
Next
I tried to use t.IsSubclassOf(typeof(IEnumerable)) and didn't have any luck either.
Code
/// <summary>
/// Reflects an enumerable (not a list, bad name should be fixed later maybe?)
/// </summary>
/// <param name="o">The Object the property resides on.</param>
/// <param name="p">The Property We're reflecting on</param>
/// <param name="rla">The Attribute tagged to this property</param>
public void ReflectList(object o, PropertyInfo p, ReflectedListAttribute rla)
{
Type t = p.PropertyType;
//if (t.IsAssignableFrom(typeof(IEnumerable)))
if (t.IsSubclassOf(typeof(IEnumerable)))
{
IEnumerable e = p.GetValue(o, null) as IEnumerable;
int count = 0;
if (e != null)
{
foreach (object lo in e)
{
if (count >= rla.MaxRows)
break;
ReflectObject(lo, count);
count++;
}
}
}
}
The Intent
I want to basically tag lists i want to reflect through with the ReflectedListAttribute and call this function on the properties that has it. (Already Working)
Once inside this function, given the object the property resides on, and the PropertyInfo related, get the value of the property, cast it to an IEnumerable (assuming it's possible) and then iterate through each child and call ReflectObject(...) on the child with the count variable.
When you do the as IEnumerable and the variable is not null you know that it does implement IEnumerable interface.
You donĀ“t need the code:
Type t = p.PropertyType;
//if (t.IsAssignableFrom(typeof(IEnumerable)))
if (t.IsSubclassOf(typeof(IEnumerable)))
{
This would be enough:
public void ReflectList(object o, PropertyInfo p, ReflectedListAttribute rla)
{
IEnumerable e = p.GetValue(o, null) as IEnumerable;
int count = 0;
if (e != null)
{
foreach (object lo in e)
{
if (count >= rla.MaxRows)
break;
ReflectObject(lo, count);
count++;
}
}
}
From MSDN
The IsSubclassOf method cannot be used
to determine whether an interface
derives from another interface, or
whether a class implements an
interface Use the GetInterface method for that purpose
Also your implementation of IsAssignableFrom is wrong, you should use it like this:
typeof(IEnumerable).IsAssignableFrom(t)
This should return true if IEnumerable is implemented by t..
Why do you the if-statement at all?
You already did a var e = ... as IEnumerable and afterwards just check if is not null.
Isn't that enough?
These work. :)
A List is extended Collection. So, the tests are different for them. A Dictionary has got two internal containers. Hence one test for the same.
public static bool IsList(object obj)
{
System.Collections.IList list = obj as System.Collections.IList;
return list != null;
}
public static bool IsCollection(object obj)
{
System.Collections.ICollection coll = obj as System.Collections.ICollection;
return coll != null;
}
public static bool IsDictionary(object obj)
{
System.Collections.IDictionary dictionary = obj as System.Collections.IDictionary;
return dictionary != null;
}
Usage example -
if (IsDictionary(fieldObject)) //key-value type collection?
{
System.Collections.IDictionary dictionary = fieldObject as System.Collections.IDictionary;
foreach (object _ob in dictionary.Values)
{
//do work
}
// dictionary.Clear();
}
else //normal collection
{
if (IsCollection(fieldObject))
{
System.Collections.ICollection coll = fieldObject as System.Collections.ICollection;
foreach (object _ob in coll)
{
//do work
}
if (IsList(fieldObject))
{
//System.Collections.IList list = fieldObject as System.Collections.IList;
//list.Clear(); // <--- List's function, not Collection's.
}
}
}

Categories