Related
I'm using reflection to loop through a Type's properties and set certain types to their default. Now, I could do a switch on the type and set the default(Type) explicitly, but I'd rather do it in one line. Is there a programmatic equivalent of default?
In case of a value type use Activator.CreateInstance and it should work fine.
When using reference type just return null
public static object GetDefault(Type type)
{
if(type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
In the newer version of .net such as .net standard, type.IsValueType needs to be written as type.GetTypeInfo().IsValueType
Why not call the method that returns default(T) with reflection ? You can use GetDefault of any type with:
public object GetDefault(Type t)
{
return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
}
public T GetDefaultGeneric<T>()
{
return default(T);
}
You can use PropertyInfo.SetValue(obj, null). If called on a value type it will give you the default. This behavior is documented in .NET 4.0 and in .NET 4.5.
If you're using .NET 4.0 or above and you want a programmatic version that isn't a codification of rules defined outside of code, you can create an Expression, compile and run it on-the-fly.
The following extension method will take a Type and get the value returned from default(T) through the Default method on the Expression class:
public static T GetDefaultValue<T>()
{
// We want an Func<T> which returns the default.
// Create that expression here.
Expression<Func<T>> e = Expression.Lambda<Func<T>>(
// The default value, always get what the *code* tells us.
Expression.Default(typeof(T))
);
// Compile and return the value.
return e.Compile()();
}
public static object GetDefaultValue(this Type type)
{
// Validate parameters.
if (type == null) throw new ArgumentNullException("type");
// We want an Func<object> which returns the default.
// Create that expression here.
Expression<Func<object>> e = Expression.Lambda<Func<object>>(
// Have to convert to object.
Expression.Convert(
// The default value, always get what the *code* tells us.
Expression.Default(type), typeof(object)
)
);
// Compile and return the value.
return e.Compile()();
}
You should also cache the above value based on the Type, but be aware if you're calling this for a large number of Type instances, and don't use it constantly, the memory consumed by the cache might outweigh the benefits.
Why do you say generics are out of the picture?
public static object GetDefault(Type t)
{
Func<object> f = GetDefault<object>;
return f.Method.GetGenericMethodDefinition().MakeGenericMethod(t).Invoke(null, null);
}
private static T GetDefault<T>()
{
return default(T);
}
This is optimized Flem's solution:
using System.Collections.Concurrent;
namespace System
{
public static class TypeExtension
{
//a thread-safe way to hold default instances created at run-time
private static ConcurrentDictionary<Type, object> typeDefaults =
new ConcurrentDictionary<Type, object>();
public static object GetDefaultValue(this Type type)
{
return type.IsValueType
? typeDefaults.GetOrAdd(type, Activator.CreateInstance)
: null;
}
}
}
The chosen answer is a good answer, but be careful with the object returned.
string test = null;
string test2 = "";
if (test is string)
Console.WriteLine("This will never be hit.");
if (test2 is string)
Console.WriteLine("Always hit.");
Extrapolating...
string test = GetDefault(typeof(string));
if (test is string)
Console.WriteLine("This will never be hit.");
I do the same task like this.
//in MessageHeader
private void SetValuesDefault()
{
MessageHeader header = this;
Framework.ObjectPropertyHelper.SetPropertiesToDefault<MessageHeader>(this);
}
//in ObjectPropertyHelper
public static void SetPropertiesToDefault<T>(T obj)
{
Type objectType = typeof(T);
System.Reflection.PropertyInfo [] props = objectType.GetProperties();
foreach (System.Reflection.PropertyInfo property in props)
{
if (property.CanWrite)
{
string propertyName = property.Name;
Type propertyType = property.PropertyType;
object value = TypeHelper.DefaultForType(propertyType);
property.SetValue(obj, value, null);
}
}
}
//in TypeHelper
public static object DefaultForType(Type targetType)
{
return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
}
Equivalent to Dror's answer but as an extension method:
namespace System
{
public static class TypeExtensions
{
public static object Default(this Type type)
{
object output = null;
if (type.IsValueType)
{
output = Activator.CreateInstance(type);
}
return output;
}
}
}
The Expressions can help here:
private static Dictionary<Type, Delegate> lambdasMap = new Dictionary<Type, Delegate>();
private object GetTypedNull(Type type)
{
Delegate func;
if (!lambdasMap.TryGetValue(type, out func))
{
var body = Expression.Default(type);
var lambda = Expression.Lambda(body);
func = lambda.Compile();
lambdasMap[type] = func;
}
return func.DynamicInvoke();
}
I did not test this snippet, but i think it should produce "typed" nulls for reference types..
Slight adjustments to #Rob Fonseca-Ensor's solution: The following extension method also works on .Net Standard since I use GetRuntimeMethod instead of GetMethod.
public static class TypeExtensions
{
public static object GetDefault(this Type t)
{
var defaultValue = typeof(TypeExtensions)
.GetRuntimeMethod(nameof(GetDefaultGeneric), new Type[] { })
.MakeGenericMethod(t).Invoke(null, null);
return defaultValue;
}
public static T GetDefaultGeneric<T>()
{
return default(T);
}
}
...and the according unit test for those who care about quality:
[Fact]
public void GetDefaultTest()
{
// Arrange
var type = typeof(DateTime);
// Act
var defaultValue = type.GetDefault();
// Assert
defaultValue.Should().Be(default(DateTime));
}
/// <summary>
/// returns the default value of a specified type
/// </summary>
/// <param name="type"></param>
public static object GetDefault(this Type type)
{
return type.IsValueType ? (!type.IsGenericType ? Activator.CreateInstance(type) : type.GenericTypeArguments[0].GetDefault() ) : null;
}
This should work:
Nullable<T> a = new Nullable<T>().GetValueOrDefault();
If you already create an object, you can try...
var yourObj = new yourObj();
var properties = yourObj.GetType().GetProperties();
foreach (var p in properties)
{
// you can get default value for each property
var defaultValue = p.GetValue(yourObj, null);
}
I'm creating a function to loop over an object and its childs.
But I'm having some issue when i try to map from the original object to the new object, here is the code:
public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{
try
{
var objectroutelist = objectRoute.Split('.');
var objroute = objectroutelist.First();
var childproperty = typeof(T).GetProperty(objroute);
if (objectroutelist.Count() == 1)
{
if (childproperty.GetValue(obj).ToString().Trim() == value)
{
return true;
}
return false;
}
else
{
var instance = Activator.CreateInstance(childproperty.PropertyType);
//childproperty.SetValue(obj, instance, null);
//childproperty.SetValue(instance, obj, null);
instance.MapValues(childproperty);
instance.MatchObjectField(string.Join(".", objectroutelist.Skip(1)), value);
}
}
catch (Exception e)
{
return false;
}
return false;
}
And here the class where I do the map and contains the issue.
public static void MapValues<T>(this T destination, PropertyInfo orgproperty)
{
var orgvalues = orgproperty.GetPropertiesWithValues();
var instance = Activator.CreateInstance(typeof(T));
foreach (var property in (typeof(T)).GetProperties())
{
try
{
var value = orgvalues.FirstOrDefault(a => a.Key == property.Name);
property.SetValue(instance, value);
}
catch (Exception)
{
property.SetValue(instance, null);
}
}
destination = (T)(object)instance;
}
The idea of the function is call with objectName.MatchObjectField("parent.child.child.child","MyName")
When I try to compare a field in the parent like objectName.MatchObjectField("Country","Ireland") it works perfectly
But when I try to create the child structure doing a call like objectName.MatchObjectField("Country.Address.City","Dublin") it breaks when I try to map to the new object.
What I noticed is that the property destination into the method MapValues<T> is mapped as Country.Address with all the properties as null which is correct.
But (typeof(T)).GetProperties() doesn't return anything.
Also i noticed that Activator.CreateInstance(typeof(T)) retunrs type {object} instead of return Country.Address
that may be the reason why is not working.
Any idea how to fix this?
EDIT: add get properties with values-> it retuns a Dictionary<string, object>
public static Dictionary<string, object> GetPropertiesWithValues(this Object obj, bool includeValueTypes = true)
{
return InternalGetProperties(obj, false, includeValueTypes);
}
private static Dictionary<string, object> InternalGetProperties(Object obj, bool withDefaultValue, bool includeValueTypes = true)
{
Dictionary<string, object> d = new Dictionary<string, object>();
var res = GetPropertiesR(obj, d, "", withDefaultValue, includeValueTypes);
return res;
}
private static Dictionary<string, object> GetPropertiesR(Object obj, Dictionary<string, object> d, string parent, bool searchingForDefaults, bool includeValueTypes)
{
if (obj == null)
return d;
var pis = #obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (pis == null || pis.Length == 0)
throw new InvalidOperationException("This object doens't have inner properties");
Func<string, string> formatProperty = (property) => string.Concat(parent, parent == "" ? "" : ".", property);
foreach (var pi in pis)
{
object data = pi.GetValue(#obj, null);
// check if is value type
if (pi.PropertyType.IsValueType)
{
// data is never null
if (!includeValueTypes)
continue;
Type nullableType = Nullable.GetUnderlyingType(pi.PropertyType);
object defaultValue = nullableType != null ? GetDefault(nullableType) : GetDefault(data.GetType());
if (!searchingForDefaults)
{
// check default values.
if (data != null && data.ToString() != defaultValue.ToString())
d.Add(formatProperty(pi.Name), data);
}
else
{
// check default values
if ((nullableType != null && data == null) || data.ToString() == defaultValue.ToString())
d.Add(formatProperty(pi.Name), data);
}
}
else
{
//
// reference types
if (!searchingForDefaults)
{
if (data == default(object))
continue;
if (IsThisPropertyPartofSystemNamespace(pi))
{
// transform list into a string with values.
IEnumerable enumeration = data as IList;
if (enumeration != null)
{
StringBuilder sb = new StringBuilder();
foreach (var i in enumeration)
sb.Append(string.Concat(i.ToString(), ", "));
if (sb.Length >= 2)
sb.Remove(sb.Length - 2, 2);
data = sb.ToString();
}
d.Add(formatProperty(pi.Name), data);
}
else
{
//
// user complex type defined
string ctxParent = string.Concat(parent, parent == "" ? "" : ".", pi.Name);
GetPropertiesR(data, d, ctxParent, searchingForDefaults, includeValueTypes);
}
}
else
{
if (data != default(object))
continue;
d.Add(formatProperty(pi.Name), data);
}
}
}
return d;
}
private static bool IsThisPropertyPartofSystemNamespace(PropertyInfo pi)
{
var systemNames = new HashSet<string>
{
"mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}",
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}"
};
var isSystemType = systemNames.Contains(pi.PropertyType.Assembly.FullName);
return isSystemType;
}
private static object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
TL;DR: Take a step back. Describe the responsibility of each of the methods separately, and write tests for them that way. Start with the "lowest level" method and work your way up. That will make it a lot easier to see what's going wrong.
There are multiple problems here. First look at these two lines of code:
var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues(childproperty);
The return type of Activator.CreateInstance is object, so this is effectively:
var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues<object>(childproperty);
That's not what you want - you want to use childproperty.PropertyType as the type argument to MapValues. You don't know that at compile-time, so it's not a good fit as a type parameter.
But beyond that, your MapValues method has a bigger problem: it basically ignores its parameter. The only time it uses destination is in the last line, when it assigns a new value to it:
destination = (T)(object)instance;
That parameter is a value parameter, so assigning it at the end of the method is pointless.
You should decide what the purpose of MapValues is:
Is it to create an instance and populate it, then return it?
Is it to accept an existing instance and populate that?
Either is simple enough to implement, but at the moment you're half way between the two. Also note that you're only passing in a single PropertyInfo - consider how you'd expect to assign multiple properties with that.
Finally, there's the matter of where the values come from. You're currently calling GetPropertiesWithValues on the PropertyInfo - that's not going to do what I think you expect it to. You need to provide the source object itself, otherwise there's nowhere to get values from.
I found what i need to do in this post: Getting Nested Object Property Value Using Reflection
getting the nested object is an easy way of what i was pretending to do.
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if(propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{
try
{
var propvalue = GetPropertyValue(obj, objectRoute);
return ( propvalue.ToString().Trim() == value.Trim());
}
catch (Exception) {
throw;
}
}
I have a need to update object A's property if null with that from object B's equivalent property if that is not null. I wanted code I can use for various objects.
I had a version working until one of the objects contained a propert of type List, which is where I have a blank in the code below. My main question is how can I best implement this part of the code. Secondly is there a better way of doing this whole thing and thirdly I know its never going to be rapid but any suggestions to speed it up would be appreciated.
Thanks in advance.
public T MergeWith<T, U>(T primarySource, U secondarySource) where U : class, T
{
Type primaryType = typeof(T);
Type secondaryType = typeof(U);
foreach (PropertyInfo primaryInfo in primaryType.GetProperties())
{
if (primaryInfo.CanWrite)
{
object currentPrimary = primaryInfo.GetValue(primarySource, null);
PropertyInfo secondaryInfo = secondaryType.GetProperty(primaryInfo.Name);
object currentSecondary = secondaryInfo.GetValue(secondarySource, null);
if (currentPrimary == null && currentSecondary != null)
{
primaryInfo.SetValue(primarySource, currentSecondary, null);
}
else if ((currentPrimary != null && currentSecondary != null) && isChildClass(primaryInfo))
{
if (isCollection(currentPrimary))
{
// here
}
else
{
MethodInfo method = typeof(NavigationModel).GetMethod("MergeWith");
MethodInfo generic = method.MakeGenericMethod(primaryInfo.PropertyType, primaryInfo.PropertyType);
object returnChild = generic.Invoke(this, new object[2] { currentPrimary, currentSecondary });
}
}
}
}
return primarySource;
}
private bool isCollection(object property)
{
return typeof(ICollection).IsAssignableFrom(property.GetType())
|| typeof(ICollection<>).IsAssignableFrom(property.GetType());
}
private bool isChildClass(PropertyInfo propertyInfo)
{
return (propertyInfo.PropertyType.IsClass && !propertyInfo.PropertyType.IsValueType &&
!propertyInfo.PropertyType.IsPrimitive && propertyInfo.PropertyType.FullName != "System.String");
}
I have created the below extension method for use in my latest project and it works fine, collections and all. It is a pretty much a simpler version of what you are doing in your method. With mine both classes have to be the same type. What problem do you encounter with collections?
public static class ExtensionMethods
{
public static TEntity CopyTo<TEntity>(this TEntity OriginalEntity, TEntity NewEntity)
{
PropertyInfo[] oProperties = OriginalEntity.GetType().GetProperties();
foreach (PropertyInfo CurrentProperty in oProperties.Where(p => p.CanWrite))
{
if (CurrentProperty.GetValue(NewEntity, null) != null)
{
CurrentProperty.SetValue(OriginalEntity, CurrentProperty.GetValue(NewEntity, null), null);
}
}
return OriginalEntity;
}
}
Hi I modified Ben Robinsons solution in order to not overwrite Collections or list, instead, it adds the elements of one object to the other one where the merging is happening:
public static class ExtensionMethods
{
public static TEntity CopyTo<TEntity>(this TEntity OriginalEntity, TEntity EntityToMergeOn)
{
PropertyInfo[] oProperties = OriginalEntity.GetType().GetProperties();
foreach (PropertyInfo CurrentProperty in oProperties.Where(p => p.CanWrite))
{
var originalValue = CurrentProperty.GetValue(EntityToMergeOn);
if (originalValue != null)
{
IListLogic<TEntity>(OriginalEntity, CurrentProperty, originalValue);
}
else
{
var value = CurrentProperty.GetValue(OriginalEntity, null);
CurrentProperty.SetValue(EntityToMergeOn, value, null);
}
}
return OriginalEntity;
}
private static void IListLogic<TEntity>(TEntity OriginalEntity, PropertyInfo CurrentProperty, object originalValue)
{
if (originalValue is IList)
{
var tempList = (originalValue as IList);
var existingList = CurrentProperty.GetValue(OriginalEntity) as IList;
foreach (var item in tempList)
{
existingList.Add(item);
}
}
}
}
I have the following class:
class CrmToRealTypeConverter : IConverter
{
#region IConverter Members
public object Convert<T>(T obj)
{
return Convert(obj);
}
#endregion
private DateTime? Convert(CrmDateTime obj)
{
return obj.IsNull == false ? (DateTime?)obj.UserTime : null;
}
private int? Convert(CrmNumber obj)
{
return obj.IsNull == false ? (int?)obj.Value : null;
}
private decimal? Convert(CrmDecimal obj)
{
return obj.IsNull == false ? (decimal?)obj.Value : null;
}
private double? Convert(CrmDouble obj)
{
return obj.IsNull == false ? (double?)obj.Value : null;
}
private float? Convert(CrmFloat obj)
{
return obj.IsNull == false ? (float?)obj.Value : null;
}
private decimal? Convert(CrmMoney obj)
{
return obj.IsNull == false ? (decimal?)obj.Value : null;
}
private bool? Convert(CrmBoolean obj)
{
return obj.IsNull == false ? (bool?)obj.Value : null;
}
}
I am trying to specialize the Convert method with concreate types.
Currently it just loops recursively in Convert<T>() until a stack overflow occurs.
Polymorphism doesn't work on arguments to a method call. An approach you can use it to check the type of obj, cast it to the specific type and then call the appropriate overload.
public object Convert(object obj)
{
if (obj is CrmDateTime)
return Convert((CrmDateTime)obj);
if (obj is CrmNumber)
return Convert((CrmNumber)obj);
// ...
}
Late-binding doesn't happen the way you think it does; the compiler binds the call to Convert(obj) in thepublic object Convert<T>(T obj) method to the same method (recursive call). The behaviour you appear to be expecting is that the CLR will dynamically choose the most appropriate overload to execute at run-time, but it doesn't work that way. Try something like this instead:
public object Convert<T>(T obj)
{
if (obj == null)
throw new ArgumentNullException("obj");
var cdt = obj as CrmDateTime;
if (cdt != null)
return Convert(cdt); // bound at compile-time to DateTime? Convert(CrmDateTime)
var cn = obj as CrmNumber;
if (cn != null)
return Convert(cn); // bound at compile-time to int? Convert(CrmNumber)
// ...
throw new NotSupportedException("Cannot convert " + obj.GetType());
}
If you prefer, you can use reflection here. Such a solution would look something like:
// Making the method generic doesn't really help
public object Convert(object obj)
{
if (obj == null)
throw new ArgumentNullException("obj");
// Target method is always a private, instance method
var bFlags = BindingFlags.Instance | BindingFlags.NonPublic;
// ..which takes a parameter of the obj's type.
var parameterTypes = new[] { obj.GetType() };
// Get a MethodInfo instance that represents the correct overload
var method = typeof(CrmToRealTypeConverter)
.GetMethod("Convert", bFlags, null, parameterTypes, null);
if (method == null)
throw new NotSupportedException("Cannot convert " + obj.GetType());
// Invoke the method with the forwarded argument
return method.Invoke(this, new object[] { obj });
}
The model you should follow is the model in the .Net Convert class, there is no reason for you to make the constructor a generic, it brings nothing to the table. Change the convert routines to static methods and the class itself to static:
static class CrmToRealTypeConverter : IConverter
{
#region IConverter Members
public static DateTime? Convert(CrmDateTime obj)
{
return obj.IsNull == false ? (DateTime?)obj.UserTime : null;
}
public static int? Convert(CrmNumber obj)
{
return obj.IsNull == false ? (int?)obj.Value : null;
}
public static decimal? Convert(CrmDecimal obj)
{
return obj.IsNull == false ? (decimal?)obj.Value : null;
}
public static double? Convert(CrmDouble obj)
{
return obj.IsNull == false ? (double?)obj.Value : null;
}
public static float? Convert(CrmFloat obj)
{
return obj.IsNull == false ? (float?)obj.Value : null;
}
public static decimal? Convert(CrmMoney obj)
{
return obj.IsNull == false ? (decimal?)obj.Value : null;
}
public static bool? Convert(CrmBoolean obj)
{
return obj.IsNull == false ? (bool?)obj.Value : null;
}
}
Then when you call one of the convert methods the compiler will select the proper overload to call:
CrmDateTime crmDate;
CrmToRealTypeConverter.Convert(crmDate); // Will call the static DateTime? Convert(CrmDateTime obj) overload
// or
CrmNumber crmNum;
CrmToRealTypeConverter.Convert(crmNum); // Will call the static int? Convert(CrmNumber obj) overload
// and so on...
Edit:
If you do the following:
CrmFloat num;
// ...
Object obj = num;
CrmToRealTypeConverter.Convert(obj);
it will not work as the compiler doesn't know the type to match the overload. You would have to cast it and it will work:
CrmToRealTypeConverter.Convert((CrmFloat)obj);
This occurs because the compiler doesn't know the generic type of T until runtime and binds the call to T = System.Object at compile-time, and the only function suitable to take a System.Object is that function itself. However, in .NET 4, you can use the dynamic keyword to cause the runtime to select the correct overload dynamically based on T at runtime, which is what you are looking to happen.
Simple example:
class Main {
static void somefunction(System.String str)
{
System.Console.WriteLine("In String overload");
}
static void somefunction(System.Object obj)
{
System.Console.WriteLine("In Object overload");
}
static void somegenericfunction<T>(T object)
{
somefunction(object);
}
static void dynamicfunction<T>(dynamic T object)
{
somefunction(object);
}
static void main(System.String[] args)
{
somegenericfunction("A string"); // Calls Object overload, even though it's a String.
dynamicfunction("A string"); // Calls String overload
}
}
Note that I don't actually have my compiler on hand and this might not compile literally, but close enough.
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>>