Merge branch 'dev-v7' into 7.3.0

This commit is contained in:
Shannon
2015-02-04 12:14:45 +11:00
7 changed files with 473 additions and 249 deletions

View File

@@ -5,7 +5,6 @@ using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Linq.Expressions;
using System.Web.Services.Description;
using Umbraco.Core.Cache;
namespace Umbraco.Core.Dynamics
@@ -18,7 +17,7 @@ namespace Umbraco.Core.Dynamics
/// <summary>
/// The static cache for extension methods found that match the criteria that we are looking for
/// </summary>
private static readonly ConcurrentDictionary<Tuple<Type, string, int>, MethodInfo[]> MethodCache = new ConcurrentDictionary<Tuple<Type, string, int>, MethodInfo[]>();
private static readonly ConcurrentDictionary<Tuple<Type, string, int>, ExtensionMethodSearchResult[]> MethodCache = new ConcurrentDictionary<Tuple<Type, string, int>, ExtensionMethodSearchResult[]>();
/// <summary>
/// Returns the enumerable of all extension method info's in the app domain = USE SPARINGLY!!!
@@ -66,46 +65,66 @@ namespace Umbraco.Core.Dynamics
/// <param name="argumentCount">
/// The arguments EXCLUDING the 'this' argument in an extension method
/// </param>
/// <returns></returns>
/// <returns>
/// Returns the MethodInfo that matched + the generic type
/// </returns>
/// <remarks>
/// NOTE: This will be an intensive method to call! Results will be cached based on the key (args) of this method
/// </remarks>
internal static IEnumerable<MethodInfo> GetAllExtensionMethods(IRuntimeCacheProvider runtimeCache, Type thisType, string name, int argumentCount)
internal static IEnumerable<ExtensionMethodSearchResult> GetAllExtensionMethods(IRuntimeCacheProvider runtimeCache, Type thisType, string name, int argumentCount)
{
var key = new Tuple<Type, string, int>(thisType, name, argumentCount);
return MethodCache.GetOrAdd(key, tuple =>
{
return MethodCache.GetOrAdd(key, tuple =>
{
var candidates = GetAllExtensionMethodsInAppDomain(runtimeCache);
// filter by name
var filtr1 = candidates.Where(m => m.Name == name);
// filter by args count
// ensure we add + 1 to the arg count because the 'this' arg is not included in the count above!
var filtr2 = filtr1.Where(m => m.GetParameters().Length == argumentCount + 1);
//filter by name
var methodsByName = candidates.Where(m => m.Name == name);
// filter by first parameter type (target of the extension method)
// ie find the right overload that can take genericParameterType
// (which will be either DynamicNodeList or List<DynamicNode> which is IEnumerable)
var filtr3 = filtr2.Select(x =>
{
var t = x.GetParameters()[0].ParameterType; // exists because of +1 above
var bindings = new Dictionary<string, List<Type>>();
if (thisType.MatchType(t, bindings) == false) return null;
//ensure we add + 1 to the arg count because the 'this' arg is not included in the count above!
var byParamCount = methodsByName.Where(m => m.GetParameters().Length == argumentCount + 1);
// create the generic method if necessary
if (x.ContainsGenericParameters == false) return x;
var targs = t.GetGenericArguments().Select(y => bindings[y.Name].First()).ToArray();
return x.MakeGenericMethod(targs);
}).Where(x => x != null);
var methodsWithFirstParamType = byParamCount.Select(methodInfo => new {methodInfo = methodInfo, firstParamType = FirstParameterType(methodInfo)})
.ToArray(); // get the array so we don't keep enumerating.
return filtr3.ToArray();
});
//Find the method with an assignable first parameter type - this is an easy match
var methodsWhereArgZeroIsTargetType = methodsWithFirstParamType
.Where(method => method.firstParamType != null && TypeHelper.IsTypeAssignableFrom(method.firstParamType, thisType))
.ToArray();
//found some so return this before trying to look for generics
if (methodsWhereArgZeroIsTargetType.Any())
return methodsWhereArgZeroIsTargetType.Select(mt => new ExtensionMethodSearchResult(mt.methodInfo)).ToArray();
//this is where it gets tricky, none of the first parameters were assignable to our type which means that
// if they are assignable they are generic arguments
return methodsWithFirstParamType
.Where(method => method.firstParamType != null)
.Select(x => new
{
methodInfo = x.methodInfo,
genericMethodResult = TypeHelper.IsAssignableFromGeneric(x.firstParamType, thisType)
})
.Where(x => x.genericMethodResult.Success)
.Select(x => new ExtensionMethodSearchResult(x.methodInfo, x.genericMethodResult.Result))
.ToArray();
});
}
private static Type FirstParameterType(MethodInfo m)
{
var p = m.GetParameters();
if (p.Any())
{
return p.First().ParameterType;
}
return null;
}
private static MethodInfo DetermineMethodFromParams(IEnumerable<MethodInfo> methods, Type genericType, IEnumerable<object> args)
private static MethodInfo DetermineMethodFromParams(IEnumerable<ExtensionMethodSearchResult> methods, Type thisType, IEnumerable<object> args)
{
MethodInfo methodToExecute = null;
@@ -118,9 +137,9 @@ namespace Umbraco.Core.Dynamics
var methodsWithArgTypes = methods.Select(method => new
{
method,
method = method,
//skip the first arg because that is the extension method type ('this') that we are looking for
types = method.GetParameters().Select(pi => pi.ParameterType).Skip(1)
types = method.MethodInfo.GetParameters().Select(pi => pi.ParameterType).Skip(1)
});
//This type comparer will check
@@ -138,20 +157,23 @@ namespace Umbraco.Core.Dynamics
if (firstMatchingOverload != null)
{
methodToExecute = firstMatchingOverload.method;
methodToExecute = firstMatchingOverload.method.MethodInfo;
//if this is null then we can't need to create the generic method
if (methodToExecute.IsGenericMethodDefinition && firstMatchingOverload.method.BaseGenericTypeMatch.IsGenericType)
{
//This should never be null in this case
if (firstMatchingOverload.method.BaseGenericTypeMatch == null) throw new InvalidOperationException("No base generic type could be resolved");
methodToExecute = methodToExecute.MakeGenericMethod(firstMatchingOverload.method.BaseGenericTypeMatch.GetGenericArguments());
}
}
return methodToExecute;
return methodToExecute;
}
public static MethodInfo FindExtensionMethod(IRuntimeCacheProvider runtimeCache, Type thisType, object[] args, string name, bool argsContainsThis)
{
Type genericType = null;
if (thisType.IsGenericType)
{
genericType = thisType.GetGenericArguments()[0];
}
args = args
//if the args contains 'this', remove the first one since that is 'this' and we don't want to use
//that in the method searching
@@ -159,8 +181,34 @@ namespace Umbraco.Core.Dynamics
.ToArray();
var methods = GetAllExtensionMethods(runtimeCache, thisType, name, args.Length).ToArray();
return DetermineMethodFromParams(methods, genericType, args);
return DetermineMethodFromParams(methods, thisType, args);
}
internal class ExtensionMethodSearchResult
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public ExtensionMethodSearchResult(MethodInfo methodInfo)
{
MethodInfo = methodInfo;
IsGeneric = false;
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public ExtensionMethodSearchResult(MethodInfo methodInfo, Type baseGenericTypeMatch)
{
MethodInfo = methodInfo;
IsGeneric = true;
BaseGenericTypeMatch = baseGenericTypeMatch;
}
public MethodInfo MethodInfo { get; private set; }
public bool IsGeneric { get; private set; }
public Type BaseGenericTypeMatch { get; private set; }
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Factories
{
internal void AddAdditionalData(UmbracoEntity entity, IDictionary<string, object> originalEntityProperties)
{
var entityProps = TypeHelper.GetPublicProperties(typeof(IUmbracoEntity)).Select(x => x.Name).ToArray();
var entityProps = typeof(IUmbracoEntity).GetPublicProperties().Select(x => x.Name).ToArray();
//figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data
foreach (var k in originalEntityProperties.Keys

View File

@@ -131,20 +131,7 @@ namespace Umbraco.Core
return true;
}
// that method is broken (will return duplicates) and useless (GetInterfaces already does the job)
//public static IEnumerable<Type> AllInterfaces(this Type target)
//{
// foreach (var IF in target.GetInterfaces())
// {
// yield return IF;
// foreach (var childIF in IF.AllInterfaces())
// {
// yield return childIF;
// }
// }
//}
public static IEnumerable<Type> GetBaseTypes(this Type type, bool andSelf)
{
if (andSelf)
@@ -308,6 +295,53 @@ namespace Umbraco.Core
| BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
}
/// <summary>
/// Returns all public properties including inherited properties even for interfaces
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>
/// taken from http://stackoverflow.com/questions/358835/getproperties-to-return-all-properties-for-an-interface-inheritance-hierarchy
/// </remarks>
public static PropertyInfo[] GetPublicProperties(this Type type)
{
if (type.IsInterface)
{
var propertyInfos = new List<PropertyInfo>();
var considered = new List<Type>();
var queue = new Queue<Type>();
considered.Add(type);
queue.Enqueue(type);
while (queue.Count > 0)
{
var subType = queue.Dequeue();
foreach (var subInterface in subType.GetInterfaces())
{
if (considered.Contains(subInterface)) continue;
considered.Add(subInterface);
queue.Enqueue(subInterface);
}
var typeProperties = subType.GetProperties(
BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.Instance);
var newPropertyInfos = typeProperties
.Where(x => !propertyInfos.Contains(x));
propertyInfos.InsertRange(0, newPropertyInfos);
}
return propertyInfos.ToArray();
}
return type.GetProperties(BindingFlags.FlattenHierarchy
| BindingFlags.Public | BindingFlags.Instance);
}
/// <summary>
/// Determines whether the specified actual type is type.
/// </summary>
@@ -389,100 +423,6 @@ namespace Umbraco.Core
}
#region Match Type
private static void ReduceGenericParameterCandidateTypes(ICollection<Type> allStuff, Type type)
{
var at1 = new List<Type>();
var t = type;
while (t != null)
{
at1.Add(t);
t = t.BaseType;
}
var r = allStuff.Where(x => x.IsClass && at1.Contains(x) == false).ToArray();
foreach (var x in r) allStuff.Remove(x);
var ai1 = type.GetInterfaces();
if (type.IsInterface) ai1 = ai1.Union(new[] { type }).ToArray();
r = allStuff.Where(x => x.IsInterface && ai1.Contains(x) == false).ToArray();
foreach (var x in r) allStuff.Remove(x);
}
private static bool MatchGeneric(Type inst, Type type, IDictionary<string, List<Type>> bindings)
{
if (inst.IsGenericType == false) return false;
var instd = inst.GetGenericTypeDefinition();
var typed = type.GetGenericTypeDefinition();
if (instd != typed) return false;
var insta = inst.GetGenericArguments();
var typea = type.GetGenericArguments();
if (insta.Length != typea.Length) return false;
// but... there is no ZipWhile, and we have arrays anyway
//var x = insta.Zip<Type, Type, bool>(typea, (instax, typeax) => { ... });
for (var i = 0; i < insta.Length; i++)
if (MatchType(insta[i], typea[i], bindings) == false)
return false;
return true;
}
private static IEnumerable<Type> GetGenericParameterCandidateTypes(Type type)
{
yield return type;
var t = type.BaseType;
while (t != null)
{
yield return t;
t = t.BaseType;
}
foreach (var i in type.GetInterfaces())
yield return i;
}
public static bool MatchType(this Type inst, Type type)
{
return MatchType(inst, type, new Dictionary<string, List<Type>>());
}
internal static bool MatchType(this Type inst, Type type, IDictionary<string, List<Type>> bindings)
{
if (type.IsGenericType)
{
if (MatchGeneric(inst, type, bindings)) return true;
var t = inst.BaseType;
while (t != null)
{
if (MatchGeneric(t, type, bindings)) return true;
t = t.BaseType;
}
return inst.GetInterfaces().Any(i => MatchGeneric(i, type, bindings));
}
if (type.IsGenericParameter)
{
if (bindings.ContainsKey(type.Name))
{
ReduceGenericParameterCandidateTypes(bindings[type.Name], inst);
return bindings[type.Name].Count > 0;
}
bindings[type.Name] = new List<Type>(GetGenericParameterCandidateTypes(inst));
return true;
}
if (inst == type) return true;
if (type.IsClass && inst.IsClass && inst.IsSubclassOf(type)) return true;
if (type.IsInterface && inst.GetInterfaces().Contains(type)) return true;
return false;
}
#endregion
}
}

View File

@@ -131,8 +131,87 @@ namespace Umbraco.Core
return Attempt<Type>.Fail();
}
/// <summary>
/// Determines whether the type <paramref name="implementation"/> is assignable from the specified implementation <typeparamref name="TContract"/>,
/// <summary>
/// Checks if the generic type passed in can be assigned from the given type
/// </summary>
/// <param name="contract"></param>
/// <param name="implementation"></param>
/// <returns>
/// Returns an Attempt{Type} which if true will include the actual type that matched the genericType
/// being compared.
/// </returns>
/// <remarks>
/// First we need to check a special case, if the generic type is a generic definition but has no FullName, then
/// we cannot compare it with traditional means because the types will never match.
/// Generic types will not have a FullName in these cases: http://blogs.msdn.com/b/haibo_luo/archive/2006/02/17/534480.aspx
/// or when you retrieve a generic method parameter using reflection, for example, typeof(IEnumerable{}) is not IEnumerable{T} since
/// when reflected 'T' is actually something.
///
/// This is using a version modified from: http://stackoverflow.com/a/1075059/1968
/// </remarks>
public static Attempt<Type> IsAssignableFromGeneric(Type contract, Type implementation)
{
if (contract.IsGenericTypeDefinition == false && contract.FullName.IsNullOrWhiteSpace())
{
return IsTypeAssignableFromReflectedGeneric(contract, implementation);
}
var genericTypeDef = implementation.IsGenericType ? implementation.GetGenericTypeDefinition() : null;
if (genericTypeDef != null && genericTypeDef == contract)
return Attempt<Type>.Succeed(implementation);
var its = implementation.GetInterfaces();
foreach (var it in its)
{
genericTypeDef = it.IsGenericType ? it.GetGenericTypeDefinition() : null;
if (genericTypeDef != null && genericTypeDef == contract)
return Attempt<Type>.Succeed(it);
}
var baseType = implementation.BaseType;
return baseType != null
? IsAssignableFromGeneric(contract, baseType)
: Attempt<Type>.Fail();
}
/// <summary>
/// This is used in IsAssignableToGenericType
/// </summary>
/// <param name="contract">The generic type contract</param>
/// <param name="implementation"></param>
/// <returns>
/// Returns an Attempt{Type} which if true will include the actual type that matched the genericType
/// being compared.
/// </returns>
/// <remarks>
/// See remarks in method IsAssignableFromGeneric
/// </remarks>
private static Attempt<Type> IsTypeAssignableFromReflectedGeneric(Type contract, Type implementation)
{
if (implementation.IsGenericType && implementation.GetGenericTypeDefinition().Name == contract.Name && implementation.GenericTypeArguments.Length == contract.GenericTypeArguments.Length)
return Attempt<Type>.Succeed(implementation);
var its = implementation.GetInterfaces();
foreach (var it in its)
{
if (it.IsGenericType && it.GetGenericTypeDefinition().Name == contract.Name && implementation.GenericTypeArguments.Length == contract.GenericTypeArguments.Length)
{
return Attempt<Type>.Succeed(it);
}
}
var baseType = implementation.BaseType;
return baseType != null
? IsTypeAssignableFromReflectedGeneric(contract, baseType)
: Attempt<Type>.Fail();
}
/// <summary>
/// Determines whether the type <paramref name="implementation"/> is assignable from the specified implementation,
/// and caches the result across the application using a <see cref="ConcurrentDictionary{TKey,TValue}"/>.
/// </summary>
/// <param name="contract">The type of the contract.</param>
@@ -156,8 +235,20 @@ namespace Umbraco.Core
return IsTypeAssignableFrom(typeof(TContract), implementation);
}
/// <summary>
/// Determines whether the object instance <paramref name="implementation"/> is assignable from the specified implementation <typeparamref name="TContract"/>,
/// and caches the result across the application using a <see cref="ConcurrentDictionary{TKey,TValue}"/>.
/// </summary>
/// <typeparam name="TContract">The type of the contract.</typeparam>
/// <param name="implementation">The implementation.</param>
public static bool IsTypeAssignableFrom<TContract>(object implementation)
{
if (implementation == null) throw new ArgumentNullException("implementation");
return IsTypeAssignableFrom<TContract>(implementation.GetType());
}
/// <summary>
/// A cached method to determine whether <paramref name="implementation"/> represents a value type.
/// A method to determine whether <paramref name="implementation"/> represents a value type.
/// </summary>
/// <param name="implementation">The implementation.</param>
public static bool IsValueType(Type implementation)
@@ -166,7 +257,7 @@ namespace Umbraco.Core
}
/// <summary>
/// A cached method to determine whether <paramref name="implementation"/> is an implied value type (<see cref="Type.IsValueType"/>, <see cref="Type.IsEnum"/> or a string).
/// A method to determine whether <paramref name="implementation"/> is an implied value type (<see cref="Type.IsValueType"/>, <see cref="Type.IsEnum"/> or a string).
/// </summary>
/// <param name="implementation">The implementation.</param>
public static bool IsImplicitValueType(Type implementation)
@@ -174,14 +265,8 @@ namespace Umbraco.Core
return IsValueType(implementation) || implementation.IsEnum || implementation == typeof (string);
}
public static bool IsTypeAssignableFrom<TContract>(object implementation)
{
if (implementation == null) throw new ArgumentNullException("implementation");
return IsTypeAssignableFrom<TContract>(implementation.GetType());
}
/// <summary>
/// Returns a PropertyInfo from a type
/// Returns (and caches) a PropertyInfo from a type
/// </summary>
/// <param name="type"></param>
/// <param name="name"></param>
@@ -203,54 +288,7 @@ namespace Umbraco.Core
return x.Name == name;
return x.Name.InvariantEquals(name);
});
}
/// <summary>
/// Returns all public properties including inherited properties even for interfaces
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>
/// taken from http://stackoverflow.com/questions/358835/getproperties-to-return-all-properties-for-an-interface-inheritance-hierarchy
/// </remarks>
public static PropertyInfo[] GetPublicProperties(Type type)
{
if (type.IsInterface)
{
var propertyInfos = new List<PropertyInfo>();
var considered = new List<Type>();
var queue = new Queue<Type>();
considered.Add(type);
queue.Enqueue(type);
while (queue.Count > 0)
{
var subType = queue.Dequeue();
foreach (var subInterface in subType.GetInterfaces())
{
if (considered.Contains(subInterface)) continue;
considered.Add(subInterface);
queue.Enqueue(subInterface);
}
var typeProperties = subType.GetProperties(
BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.Instance);
var newPropertyInfos = typeProperties
.Where(x => !propertyInfos.Contains(x));
propertyInfos.InsertRange(0, newPropertyInfos);
}
return propertyInfos.ToArray();
}
return type.GetProperties(BindingFlags.FlattenHierarchy
| BindingFlags.Public | BindingFlags.Instance);
}
}
/// <summary>
/// Gets (and caches) <see cref="FieldInfo"/> discoverable in the current <see cref="AppDomain"/> for a given <paramref name="type"/>.
@@ -286,5 +324,104 @@ namespace Umbraco.Core
&& (includeIndexed || !y.GetIndexParameters().Any()))
.ToArray());
}
#region Match Type
//TODO: Need to determine if these methods should replace/combine/merge etc with IsTypeAssignableFrom, IsAssignableFromGeneric
private static void ReduceGenericParameterCandidateTypes(ICollection<Type> allStuff, Type type)
{
var at1 = new List<Type>();
var t = type;
while (t != null)
{
at1.Add(t);
t = t.BaseType;
}
var r = allStuff.Where(x => x.IsClass && at1.Contains(x) == false).ToArray();
foreach (var x in r) allStuff.Remove(x);
var ai1 = type.GetInterfaces();
if (type.IsInterface) ai1 = ai1.Union(new[] { type }).ToArray();
r = allStuff.Where(x => x.IsInterface && ai1.Contains(x) == false).ToArray();
foreach (var x in r) allStuff.Remove(x);
}
private static bool MatchGeneric(Type inst, Type type, IDictionary<string, List<Type>> bindings)
{
if (inst.IsGenericType == false) return false;
var instd = inst.GetGenericTypeDefinition();
var typed = type.GetGenericTypeDefinition();
if (instd != typed) return false;
var insta = inst.GetGenericArguments();
var typea = type.GetGenericArguments();
if (insta.Length != typea.Length) return false;
// but... there is no ZipWhile, and we have arrays anyway
//var x = insta.Zip<Type, Type, bool>(typea, (instax, typeax) => { ... });
for (var i = 0; i < insta.Length; i++)
if (MatchType(insta[i], typea[i], bindings) == false)
return false;
return true;
}
private static IEnumerable<Type> GetGenericParameterCandidateTypes(Type type)
{
yield return type;
var t = type.BaseType;
while (t != null)
{
yield return t;
t = t.BaseType;
}
foreach (var i in type.GetInterfaces())
yield return i;
}
public static bool MatchType(Type inst, Type type)
{
return MatchType(inst, type, new Dictionary<string, List<Type>>());
}
internal static bool MatchType(Type inst, Type type, IDictionary<string, List<Type>> bindings)
{
if (type.IsGenericType)
{
if (MatchGeneric(inst, type, bindings)) return true;
var t = inst.BaseType;
while (t != null)
{
if (MatchGeneric(t, type, bindings)) return true;
t = t.BaseType;
}
return inst.GetInterfaces().Any(i => MatchGeneric(i, type, bindings));
}
if (type.IsGenericParameter)
{
if (bindings.ContainsKey(type.Name))
{
ReduceGenericParameterCandidateTypes(bindings[type.Name], inst);
return bindings[type.Name].Count > 0;
}
bindings[type.Name] = new List<Type>(GetGenericParameterCandidateTypes(inst));
return true;
}
if (inst == type) return true;
if (type.IsClass && inst.IsClass && inst.IsSubclassOf(type)) return true;
if (type.IsInterface && inst.GetInterfaces().Contains(type)) return true;
return false;
}
#endregion
}
}

View File

@@ -202,50 +202,24 @@ namespace Umbraco.Tests.DynamicsAndReflection
Assert.AreEqual("T", t3.GetGenericArguments()[0].Name);
}
[Test]
public void MatchTypesTest()
public void Find_Generic_Enumerable_Method()
{
var bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(typeof(int).MatchType(typeof(int), bindings));
Assert.AreEqual(0, bindings.Count);
MethodInfo method;
var class1 = Enumerable.Empty<TestClass>();
bindings = new Dictionary<string, List<Type>>();
Assert.IsFalse(typeof(int).MatchType(typeof(string), bindings));
Assert.AreEqual(0, bindings.Count);
method = ExtensionMethodFinder.FindExtensionMethod(new NullCacheProvider(), typeof(IEnumerable<TestClass>), new object[] { }, "GenericMethod", false);
Assert.IsNotNull(method);
method.Invoke(null, new object[] { class1 });
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(typeof(List<int>).MatchType(typeof(System.Collections.IEnumerable), bindings));
Assert.AreEqual(0, bindings.Count);
var class2 = new TestClassCollection();
var m = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod7");
var t1 = m.GetParameters()[0].ParameterType; // List<T>
var t2 = m.GetParameters()[0].ParameterType.GetGenericArguments()[0]; // <T>
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(typeof(int).MatchType(t2, bindings));
Assert.AreEqual(1, bindings.Count);
Assert.AreEqual(typeof(int), bindings["T"].FirstOrDefault());
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(typeof(IList<int>).MatchType(t1, bindings));
Assert.AreEqual(1, bindings.Count);
Assert.AreEqual(typeof(int), bindings["T"].FirstOrDefault());
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(typeof(List<int>).MatchType(typeof(IList<int>), bindings));
Assert.AreEqual(0, bindings.Count);
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(typeof(List<int>).MatchType(t1, bindings));
Assert.AreEqual(1, bindings.Count);
Assert.AreEqual(typeof(int), bindings["T"].FirstOrDefault());
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(typeof(Dictionary<int, string>).MatchType(typeof(IDictionary<,>), bindings));
Assert.AreEqual(2, bindings.Count);
Assert.AreEqual(typeof(int), bindings["TKey"].FirstOrDefault());
Assert.AreEqual(typeof(string), bindings["TValue"].FirstOrDefault());
}
method = ExtensionMethodFinder.FindExtensionMethod(new NullCacheProvider(), typeof(TestClassCollection), new object[] { }, "GenericMethod", false);
Assert.IsNotNull(method);
method.Invoke(null, new object[] { class2 });
}
[Ignore("This is just testing the below GetMethodForArguments method - Stephen was working on this but it's not used in the core")]
[Test]
@@ -374,12 +348,22 @@ namespace Umbraco.Tests.DynamicsAndReflection
}
#endregion
public class TestClassCollection : List<TestClass>
{
}
}
#region Tests Elements
static class ExtensionMethodFinderTestsExtensions
{
public static void GenericMethod<T>(this IEnumerable<T> source)
{ }
public static void SimpleMethod(this ExtensionMethodFinderTests.TestClass source, int value)
{ }

View File

@@ -1,11 +1,18 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Odbc;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Tests.DynamicsAndReflection;
using Umbraco.Web.Cache;
using Umbraco.Web.Models;
using Umbraco.Web.Scheduling;
using UmbracoExamine.DataServices;
@@ -18,6 +25,72 @@ namespace Umbraco.Tests.Plugins
public class TypeHelperTests
{
[Test]
public void Is_Generic_Assignable()
{
var type1 = typeof (DynamicPublishedContentList);
var type2 = typeof (IEnumerable<IPublishedContent>);
var type3 = typeof(IQueryable<IPublishedContent>);
var type4 = typeof(List<IPublishedContent>);
var type5 = typeof(IEnumerable<>);
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(type2, type1));
Assert.IsFalse(TypeHelper.IsTypeAssignableFrom(type3, type1));
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(type2, type4));
//Will always fail which is correct, you cannot 'assign' IEnumerable<IPublishedContent> simply to IEnumerable<>
//Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(type5, type2));
Assert.IsTrue(TypeHelper.IsAssignableFromGeneric(type5, type2));
}
[Test]
public void Is_Assignable_To_Generic_Type()
{
//modified from: https://gist.github.com/klmr/4174727
//using a version modified from: http://stackoverflow.com/a/1075059/1968
Assert.IsTrue(TypeHelper.IsAssignableFromGeneric(typeof(Base<>), typeof(Derived<int>)));
Assert.IsTrue(TypeHelper.IsAssignableFromGeneric(typeof(IEnumerable<>), typeof(List<int>)));
Assert.IsTrue(TypeHelper.IsAssignableFromGeneric(typeof(Derived<>), typeof(Derived<int>)));
Assert.IsTrue(TypeHelper.IsAssignableFromGeneric(typeof(Base<>), typeof(Derived2<int>)));
Assert.IsTrue(TypeHelper.IsAssignableFromGeneric(typeof(IBase<>), typeof(DerivedI<int>)));
Assert.IsTrue(TypeHelper.IsAssignableFromGeneric(typeof(IBase<>), typeof(Derived2<int>)));
Assert.IsTrue(TypeHelper.IsAssignableFromGeneric(typeof(Nullable<>), typeof(int?)));
Assert.IsFalse(TypeHelper.IsAssignableFromGeneric(typeof(Object), typeof(Derived<int>)));
Assert.IsFalse(TypeHelper.IsAssignableFromGeneric(typeof(List<>), typeof(Derived<int>)));
Assert.IsFalse(TypeHelper.IsAssignableFromGeneric(typeof(IEnumerable<>), typeof(Derived<int>)));
Assert.IsFalse(TypeHelper.IsAssignableFromGeneric(typeof(Base<int>), typeof(Derived<int>)));
Assert.IsFalse(TypeHelper.IsAssignableFromGeneric(typeof(IEnumerable<int>), typeof(List<int>)));
Assert.IsFalse(TypeHelper.IsAssignableFromGeneric(typeof(Nullable<>), typeof(int)));
//This get's the "Type" from the Count extension method on IEnumerable<T>, however the type IEnumerable<T> isn't
// IEnumerable<> and it is not a generic definition, this attempts to explain that:
// http://blogs.msdn.com/b/haibo_luo/archive/2006/02/17/534480.aspx
var genericEnumerableNonGenericDefinition = typeof (Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Single(x => x.Name == "Count" && x.GetParameters().Count() == 1)
.GetParameters()
.Single()
.ParameterType;
Assert.IsTrue(TypeHelper.IsAssignableFromGeneric(genericEnumerableNonGenericDefinition, typeof(List<int>)));
}
class Base<T> { }
interface IBase<T> { }
interface IDerived<T> : IBase<T> { }
class Derived<T> : Base<T>, IBase<T> { }
class Derived2<T> : Derived<T> { }
class DerivedI<T> : IDerived<T> { }
[Test]
public void Is_Static_Class()
{
@@ -68,5 +141,50 @@ namespace Umbraco.Tests.Plugins
}
[Test]
public void MatchTypesTest()
{
var bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(TypeHelper.MatchType(typeof(int), typeof(int), bindings));
Assert.AreEqual(0, bindings.Count);
bindings = new Dictionary<string, List<Type>>();
Assert.IsFalse(TypeHelper.MatchType(typeof(int), typeof(string), bindings));
Assert.AreEqual(0, bindings.Count);
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(TypeHelper.MatchType(typeof(List<int>), typeof(System.Collections.IEnumerable), bindings));
Assert.AreEqual(0, bindings.Count);
var m = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod7");
var t1 = m.GetParameters()[0].ParameterType; // List<T>
var t2 = m.GetParameters()[0].ParameterType.GetGenericArguments()[0]; // <T>
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(TypeHelper.MatchType(typeof(int), t2, bindings));
Assert.AreEqual(1, bindings.Count);
Assert.AreEqual(typeof(int), bindings["T"].FirstOrDefault());
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(TypeHelper.MatchType(typeof(IList<int>), t1, bindings));
Assert.AreEqual(1, bindings.Count);
Assert.AreEqual(typeof(int), bindings["T"].FirstOrDefault());
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(TypeHelper.MatchType(typeof(List<int>), typeof(IList<int>), bindings));
Assert.AreEqual(0, bindings.Count);
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(TypeHelper.MatchType(typeof(List<int>), t1, bindings));
Assert.AreEqual(1, bindings.Count);
Assert.AreEqual(typeof(int), bindings["T"].FirstOrDefault());
bindings = new Dictionary<string, List<Type>>();
Assert.IsTrue(TypeHelper.MatchType(typeof(Dictionary<int, string>), typeof(IDictionary<,>), bindings));
Assert.AreEqual(2, bindings.Count);
Assert.AreEqual(typeof(int), bindings["TKey"].FirstOrDefault());
Assert.AreEqual(typeof(string), bindings["TValue"].FirstOrDefault());
}
}
}

View File

@@ -8,7 +8,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Web;
using Umbraco.Core.Cache;
using Umbraco.Core.Dynamics;
@@ -16,8 +15,6 @@ using Umbraco.Core.Models;
using Umbraco.Core;
using System.Reflection;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Strings;
using ContentType = umbraco.cms.businesslogic.ContentType;
namespace Umbraco.Web.Models
{