Remove Microsoft.Extensions.ClosedGenericMatcher.Sources
This code isn't used locally, so it will move to aspnet/AspNetCore
\n\nCommit migrated from 7305817fea
This commit is contained in:
parent
e8d84a1962
commit
2f36a1ecf9
|
|
@ -1,106 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.Extensions.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper related to generic interface definitions and implementing classes.
|
||||
/// </summary>
|
||||
internal static class ClosedGenericMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Determine whether <paramref name="queryType"/> is or implements a closed generic <see cref="Type"/>
|
||||
/// created from <paramref name="interfaceType"/>.
|
||||
/// </summary>
|
||||
/// <param name="queryType">The <see cref="Type"/> of interest.</param>
|
||||
/// <param name="interfaceType">The open generic <see cref="Type"/> to match. Usually an interface.</param>
|
||||
/// <returns>
|
||||
/// The closed generic <see cref="Type"/> created from <paramref name="interfaceType"/> that
|
||||
/// <paramref name="queryType"/> is or implements. <c>null</c> if the two <see cref="Type"/>s have no such
|
||||
/// relationship.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// This method will return <paramref name="queryType"/> if <paramref name="interfaceType"/> is
|
||||
/// <c>typeof(KeyValuePair{,})</c>, and <paramref name="queryType"/> is
|
||||
/// <c>typeof(KeyValuePair{string, object})</c>.
|
||||
/// </remarks>
|
||||
public static Type ExtractGenericInterface(Type queryType, Type interfaceType)
|
||||
{
|
||||
if (queryType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(queryType));
|
||||
}
|
||||
|
||||
if (interfaceType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(interfaceType));
|
||||
}
|
||||
|
||||
if (IsGenericInstantiation(queryType, interfaceType))
|
||||
{
|
||||
// queryType matches (i.e. is a closed generic type created from) the open generic type.
|
||||
return queryType;
|
||||
}
|
||||
|
||||
// Otherwise check all interfaces the type implements for a match.
|
||||
// - If multiple different generic instantiations exists, we want the most derived one.
|
||||
// - If that doesn't break the tie, then we sort alphabetically so that it's deterministic.
|
||||
//
|
||||
// We do this by looking at interfaces on the type, and recursing to the base type
|
||||
// if we don't find any matches.
|
||||
return GetGenericInstantiation(queryType, interfaceType);
|
||||
}
|
||||
|
||||
private static bool IsGenericInstantiation(Type candidate, Type interfaceType)
|
||||
{
|
||||
return
|
||||
candidate.GetTypeInfo().IsGenericType &&
|
||||
candidate.GetGenericTypeDefinition() == interfaceType;
|
||||
}
|
||||
|
||||
private static Type GetGenericInstantiation(Type queryType, Type interfaceType)
|
||||
{
|
||||
Type bestMatch = null;
|
||||
var interfaces = queryType.GetInterfaces();
|
||||
foreach (var @interface in interfaces)
|
||||
{
|
||||
if (IsGenericInstantiation(@interface, interfaceType))
|
||||
{
|
||||
if (bestMatch == null)
|
||||
{
|
||||
bestMatch = @interface;
|
||||
}
|
||||
else if (StringComparer.Ordinal.Compare(@interface.FullName, bestMatch.FullName) < 0)
|
||||
{
|
||||
bestMatch = @interface;
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are two matches at this level of the class hierarchy, but @interface is after
|
||||
// bestMatch in the sort order.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMatch != null)
|
||||
{
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
// BaseType will be null for object and interfaces, which means we've reached 'bottom'.
|
||||
var baseType = queryType?.GetTypeInfo().BaseType;
|
||||
if (baseType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetGenericInstantiation(baseType, interfaceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,360 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.Internal
|
||||
{
|
||||
public class ClosedGenericMatcherTest
|
||||
{
|
||||
// queryType, interfaceType, expectedResult
|
||||
public static TheoryData<Type, Type, Type> ExtractGenericInterfaceDataSet
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<Type, Type, Type>
|
||||
{
|
||||
// Closed generic types that match given open generic type.
|
||||
{
|
||||
typeof(IEnumerable<BaseClass>),
|
||||
typeof(IEnumerable<>),
|
||||
typeof(IEnumerable<BaseClass>)
|
||||
},
|
||||
{
|
||||
typeof(IReadOnlyList<int>),
|
||||
typeof(IReadOnlyList<>),
|
||||
typeof(IReadOnlyList<int>)
|
||||
},
|
||||
{
|
||||
typeof(KeyValuePair<string, object>),
|
||||
typeof(KeyValuePair<,>),
|
||||
typeof(KeyValuePair<string, object>)
|
||||
},
|
||||
// Closed generic interfaces that implement sub-interface of given open generic type.
|
||||
{
|
||||
typeof(ICollection<BaseClass>),
|
||||
typeof(IEnumerable<>),
|
||||
typeof(IEnumerable<BaseClass>)
|
||||
},
|
||||
{
|
||||
typeof(IReadOnlyList<int>),
|
||||
typeof(IEnumerable<>),
|
||||
typeof(IEnumerable<int>)
|
||||
},
|
||||
{
|
||||
typeof(IDictionary<string, object>),
|
||||
typeof(IEnumerable<>),
|
||||
typeof(IEnumerable<KeyValuePair<string, object>>)
|
||||
},
|
||||
// Class that implements closed generic based on given open generic interface.
|
||||
{
|
||||
typeof(BaseClass),
|
||||
typeof(IDictionary<,>),
|
||||
typeof(IDictionary<string, object>)
|
||||
},
|
||||
{
|
||||
typeof(BaseClass),
|
||||
typeof(IEquatable<>),
|
||||
typeof(IEquatable<BaseClass>)
|
||||
},
|
||||
{
|
||||
typeof(BaseClass),
|
||||
typeof(ICollection<>),
|
||||
typeof(ICollection<KeyValuePair<string, object>>)
|
||||
},
|
||||
// Derived class that implements closed generic based on given open generic interface.
|
||||
{
|
||||
typeof(DerivedClass),
|
||||
typeof(IDictionary<,>),
|
||||
typeof(IDictionary<string, object>)
|
||||
},
|
||||
{
|
||||
typeof(DerivedClass),
|
||||
typeof(IEquatable<>),
|
||||
typeof(IEquatable<BaseClass>)
|
||||
},
|
||||
{
|
||||
typeof(DerivedClass),
|
||||
typeof(ICollection<>),
|
||||
typeof(ICollection<KeyValuePair<string, object>>)
|
||||
},
|
||||
// Derived class that also implements another interface.
|
||||
{
|
||||
typeof(DerivedClassWithComparable),
|
||||
typeof(IDictionary<,>),
|
||||
typeof(IDictionary<string, object>)
|
||||
},
|
||||
{
|
||||
typeof(DerivedClassWithComparable),
|
||||
typeof(IEquatable<>),
|
||||
typeof(IEquatable<BaseClass>)
|
||||
},
|
||||
{
|
||||
typeof(DerivedClassWithComparable),
|
||||
typeof(ICollection<>),
|
||||
typeof(ICollection<KeyValuePair<string, object>>)
|
||||
},
|
||||
{
|
||||
typeof(DerivedClassWithComparable),
|
||||
typeof(IComparable<>),
|
||||
typeof(IComparable<DerivedClassWithComparable>)
|
||||
},
|
||||
// Derived class using system implementation.
|
||||
{
|
||||
typeof(DerivedClassFromSystemImplementation),
|
||||
typeof(ICollection<>),
|
||||
typeof(ICollection<BaseClass>)
|
||||
},
|
||||
{
|
||||
typeof(DerivedClassFromSystemImplementation),
|
||||
typeof(IReadOnlyList<>),
|
||||
typeof(IReadOnlyList<BaseClass>)
|
||||
},
|
||||
{
|
||||
typeof(DerivedClassFromSystemImplementation),
|
||||
typeof(IEnumerable<>),
|
||||
typeof(IEnumerable<BaseClass>)
|
||||
},
|
||||
// Not given an open generic type.
|
||||
{
|
||||
typeof(IEnumerable<BaseClass>),
|
||||
typeof(IEnumerable<BaseClass>),
|
||||
null
|
||||
},
|
||||
{
|
||||
typeof(IEnumerable<BaseClass>),
|
||||
typeof(IEnumerable),
|
||||
null
|
||||
},
|
||||
{
|
||||
typeof(IReadOnlyList<int>),
|
||||
typeof(BaseClass),
|
||||
null
|
||||
},
|
||||
{
|
||||
typeof(KeyValuePair<,>),
|
||||
typeof(KeyValuePair<string, object>),
|
||||
null
|
||||
},
|
||||
// Not a match.
|
||||
{
|
||||
typeof(IEnumerable<BaseClass>),
|
||||
typeof(IReadOnlyList<>),
|
||||
null
|
||||
},
|
||||
{
|
||||
typeof(IList<int>),
|
||||
typeof(IReadOnlyList<>),
|
||||
null
|
||||
},
|
||||
{
|
||||
typeof(IDictionary<string, object>),
|
||||
typeof(KeyValuePair<,>),
|
||||
null
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ExtractGenericInterfaceDataSet))]
|
||||
public void ExtractGenericInterface_ReturnsExpectedType(
|
||||
Type queryType,
|
||||
Type interfaceType,
|
||||
Type expectedResult)
|
||||
{
|
||||
// Arrange & Act
|
||||
var result = ClosedGenericMatcher.ExtractGenericInterface(queryType, interfaceType);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedResult, result);
|
||||
}
|
||||
|
||||
// IEnumerable<int> is preferred because it is defined on the more-derived type.
|
||||
[Fact]
|
||||
public void ExtractGenericInterface_MultipleDefinitionsInherited()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TwoIEnumerableImplementationsInherited);
|
||||
|
||||
// Act
|
||||
var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>));
|
||||
|
||||
// Sort
|
||||
Assert.Equal(typeof(IEnumerable<int>), result);
|
||||
}
|
||||
|
||||
// IEnumerable<int> is preferred because we sort by Ordinal on the full name.
|
||||
[Fact]
|
||||
public void ExtractGenericInterface_MultipleDefinitionsOnSameType()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TwoIEnumerableImplementationsOnSameClass);
|
||||
|
||||
// Act
|
||||
var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>));
|
||||
|
||||
// Sort
|
||||
Assert.Equal(typeof(IEnumerable<int>), result);
|
||||
}
|
||||
|
||||
private class TwoIEnumerableImplementationsOnSameClass : IEnumerable<string>, IEnumerable<int>
|
||||
{
|
||||
IEnumerator<int> IEnumerable<int>.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IEnumerator<string> IEnumerable<string>.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TwoIEnumerableImplementationsInherited : List<int>, IEnumerable<string>
|
||||
{
|
||||
IEnumerator<string> IEnumerable<string>.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class BaseClass : IDictionary<string, object>, IEquatable<BaseClass>
|
||||
{
|
||||
object IDictionary<string, object>.this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
int ICollection<KeyValuePair<string, object>>.Count
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<string, object>>.IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
ICollection<string> IDictionary<string, object>.Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
ICollection<object> IDictionary<string, object>.Values
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(BaseClass other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void IDictionary<string, object>.Add(string key, object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<string, object>>.Clear()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool IDictionary<string, object>.ContainsKey(string key)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool IDictionary<string, object>.Remove(string key)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool IDictionary<string, object>.TryGetValue(string key, out object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class DerivedClass : BaseClass
|
||||
{
|
||||
}
|
||||
|
||||
private class DerivedClassWithComparable : DerivedClass, IComparable<DerivedClassWithComparable>
|
||||
{
|
||||
public int CompareTo(DerivedClassWithComparable other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class DerivedClassFromSystemImplementation : Collection<BaseClass>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue