测量类型之间的关系继承距离
Measuring relational inheritance distance between types
有谁知道我如何(或者如果有现有算法)测量两个 .NET 类型之间的关系距离?
我指的是从对象 A 到对象 B 所需的层次结构树中 'steps' 的数量。
例如,如果对象A是一个Button,对象B是一个LinkButton,那么会有2个步骤,Button -> WebControl -> LinkButton。我是否需要创建自己的静态继承树并使用路径查找算法,或者是否有一种方法可以动态查看 .NET 的继承结构以计算两个对象之间的距离?
您可以使用 Type.BaseType
遍历继承路径。例如:
public static int GetTypeDistance<T, B>(T t, B baseType)
{
if (t is B) // checking if t inherits baseType
{
int distance = 0;
Type curType = t.GetType();
while (curType != typeof(B) && curType != null)
{
distance++;
curType = curType.BaseType;
}
return distance;
}
else { throw new Exception("..."); }
}
非通用方式(您也不必明确指定 parent/child):
private static int CalulateDistanceOneWay(Type firstType, Type secondType)
{
var chain = new List<Type>();
while (firstType != typeof(object))
{
chain.Add(firstType);
firstType = firstType.BaseType;
}
return chain.IndexOf(secondType);
}
// returns -1 for invalid input, distance between types otherwise
public static int CalculateDistance(Type firstType, Type secondType)
{
int result = CalulateDistanceOneWay(firstType, secondType);
if (result >= 0)
{
return result;
}
return CalulateDistanceOneWay(secondType, firstType);
}
编辑:更新计算表亲:
public class DistanceResult
{
public Type SharedAncestor { get; private set; }
public int FirstTypeDistance { get; private set; }
public int SecondTypeDistance { get; private set; }
public DistanceResult(Type sharedAncestor, int firstTypeDistance, int secondTypeDistance)
{
SharedAncestor = sharedAncestor;
FirstTypeDistance = firstTypeDistance;
SecondTypeDistance = secondTypeDistance;
}
}
static DistanceResult CalculateDistance(Type firstType, Type secondType)
{
var firstChain = new List<Type>();
while (firstType != typeof(object))
{
firstChain.Add(firstType);
firstType = firstType.BaseType;
}
firstChain.Add(typeof(object));
var secondChain = new List<Type>();
while(secondType != typeof(object))
{
secondChain.Add(secondType);
secondType = secondType.BaseType;
}
secondChain.Add(typeof(object));
for(var i = 0; i < secondChain.Count; i++)
{
var type = secondChain[i];
int index = firstChain.IndexOf(type);
if (index >= 0)
{
return new DistanceResult(firstChain[index], index, i);
}
}
return null;
}
根据 Ondrej 和 Bényi 的回答,这里有两种扩展方法来计算从特定类型到它的(间接)基础 class 类型或它实现的接口之一或其中之一的距离它的基数 classes.
使用示例:
Assert.AreEqual( 4, typeof( MultiDictionary<int, int> ).DistanceTo<IEnumerable>() );
Assert.AreEqual( 4, typeof( MultiDictionary<int, int> ).DistanceTo( typeof( IEnumerable ) );
Assert.AreEqual( 2, typeof( StringReader ).DistanceTo( typeof( IDisposable ) ) );
扩展方法:
public static class ExtensionsForType
{
public static int DistanceTo( [CanBeNull] this Type current, [NotNull] Type target )
{
Contract.Requires<ArgumentNullException>( target != null );
// `root` will point to the topmost type which is implementing
// our `target` interface
Type root = current;
// search for topmost base type implementing `target` interface type
// or for the `target` base class type itself
int distance = 0;
while ( current != null && ( target.IsInterface
? current.GetInterfaces().Contains( target )
: current != target ) )
{
root = current;
current = current.BaseType;
distance++;
}
// probably the `current` type does not even derive from / implement
// the target type at all
if ( current == null ) return -1;
// if it's not an interface then we've found it in one of the base classes
if ( !target.IsInterface ) return distance;
// go one step back, because 'current' does not implement
// our target interface anymore
distance--;
// iterate interface "derivations" while the target interface is still
// in the list of implemented interfaces
Type[] interfaces = root.GetInterfaces();
while ( interfaces.Contains( target ) )
{
interfaces = interfaces.SelectMany( i => i.GetInterfaces() ).ToArray();
distance++;
}
return distance;
}
public static int DistanceTo<T>( [CanBeNull] this Type current )
{
return current.DistanceTo( typeof( T ) );
}
}
有谁知道我如何(或者如果有现有算法)测量两个 .NET 类型之间的关系距离?
我指的是从对象 A 到对象 B 所需的层次结构树中 'steps' 的数量。
例如,如果对象A是一个Button,对象B是一个LinkButton,那么会有2个步骤,Button -> WebControl -> LinkButton。我是否需要创建自己的静态继承树并使用路径查找算法,或者是否有一种方法可以动态查看 .NET 的继承结构以计算两个对象之间的距离?
您可以使用 Type.BaseType
遍历继承路径。例如:
public static int GetTypeDistance<T, B>(T t, B baseType)
{
if (t is B) // checking if t inherits baseType
{
int distance = 0;
Type curType = t.GetType();
while (curType != typeof(B) && curType != null)
{
distance++;
curType = curType.BaseType;
}
return distance;
}
else { throw new Exception("..."); }
}
非通用方式(您也不必明确指定 parent/child):
private static int CalulateDistanceOneWay(Type firstType, Type secondType)
{
var chain = new List<Type>();
while (firstType != typeof(object))
{
chain.Add(firstType);
firstType = firstType.BaseType;
}
return chain.IndexOf(secondType);
}
// returns -1 for invalid input, distance between types otherwise
public static int CalculateDistance(Type firstType, Type secondType)
{
int result = CalulateDistanceOneWay(firstType, secondType);
if (result >= 0)
{
return result;
}
return CalulateDistanceOneWay(secondType, firstType);
}
编辑:更新计算表亲:
public class DistanceResult
{
public Type SharedAncestor { get; private set; }
public int FirstTypeDistance { get; private set; }
public int SecondTypeDistance { get; private set; }
public DistanceResult(Type sharedAncestor, int firstTypeDistance, int secondTypeDistance)
{
SharedAncestor = sharedAncestor;
FirstTypeDistance = firstTypeDistance;
SecondTypeDistance = secondTypeDistance;
}
}
static DistanceResult CalculateDistance(Type firstType, Type secondType)
{
var firstChain = new List<Type>();
while (firstType != typeof(object))
{
firstChain.Add(firstType);
firstType = firstType.BaseType;
}
firstChain.Add(typeof(object));
var secondChain = new List<Type>();
while(secondType != typeof(object))
{
secondChain.Add(secondType);
secondType = secondType.BaseType;
}
secondChain.Add(typeof(object));
for(var i = 0; i < secondChain.Count; i++)
{
var type = secondChain[i];
int index = firstChain.IndexOf(type);
if (index >= 0)
{
return new DistanceResult(firstChain[index], index, i);
}
}
return null;
}
根据 Ondrej 和 Bényi 的回答,这里有两种扩展方法来计算从特定类型到它的(间接)基础 class 类型或它实现的接口之一或其中之一的距离它的基数 classes.
使用示例:
Assert.AreEqual( 4, typeof( MultiDictionary<int, int> ).DistanceTo<IEnumerable>() );
Assert.AreEqual( 4, typeof( MultiDictionary<int, int> ).DistanceTo( typeof( IEnumerable ) );
Assert.AreEqual( 2, typeof( StringReader ).DistanceTo( typeof( IDisposable ) ) );
扩展方法:
public static class ExtensionsForType
{
public static int DistanceTo( [CanBeNull] this Type current, [NotNull] Type target )
{
Contract.Requires<ArgumentNullException>( target != null );
// `root` will point to the topmost type which is implementing
// our `target` interface
Type root = current;
// search for topmost base type implementing `target` interface type
// or for the `target` base class type itself
int distance = 0;
while ( current != null && ( target.IsInterface
? current.GetInterfaces().Contains( target )
: current != target ) )
{
root = current;
current = current.BaseType;
distance++;
}
// probably the `current` type does not even derive from / implement
// the target type at all
if ( current == null ) return -1;
// if it's not an interface then we've found it in one of the base classes
if ( !target.IsInterface ) return distance;
// go one step back, because 'current' does not implement
// our target interface anymore
distance--;
// iterate interface "derivations" while the target interface is still
// in the list of implemented interfaces
Type[] interfaces = root.GetInterfaces();
while ( interfaces.Contains( target ) )
{
interfaces = interfaces.SelectMany( i => i.GetInterfaces() ).ToArray();
distance++;
}
return distance;
}
public static int DistanceTo<T>( [CanBeNull] this Type current )
{
return current.DistanceTo( typeof( T ) );
}
}