13

使用する2つの拡張メソッドは次のとおりです

public static Type FindInterfaceWith(this Type type1, Type type2) {
    // returns most suitable common implemented interface
}

public static Type FindBaseClassWith(this Type type1, Type type2) {
    // returns most derivative of common base class
}
  • FindInterfaceWithnull共通の実装済みインターフェースがない場合に戻ります。
  • FindBaseClassWithSystem.Object微分共通基本クラスがなくなった場合に戻ります。
  • FindBaseClassWithnullパラメータの1つがインターフェイスであった場合に戻ります。
  • nullパラメータのいずれかが。だった場合、両方とも返されますnull

そして、finallyソリューションのメソッドのシグネチャは次のようになります。

public static Type FindAssignableWith(this Type type1, Type type2) {
    // what should be here?
}

ReflectionとLinqは、他に方法がない場合を除いて、使用が制限されています。

との間の一般的なタイプの最適なものを見つける良い方法はtype1ありtype2ますか?

それとも、これを達成するためのより良いものがありますか?


アップデート:

私の個人的な理解では、クラスで複数のインターフェースを実装できるため、内部FindInterfaceWithで呼び出す必要がある可能性があります。FindBaseClassWithそうしないと、タイプの最良の選択が決定不能になります。

この仮定が正しければ、FindInterfaceWithは冗長な方法になります。FindInterfaceWithとの唯一の違いFindAssignableWithは次のとおりです。

FindInterfaceWithnullクラスの最良の選択があった場合に戻ります。whileFindAssignableWithは、正確なクラスを直接返します。

それ以外の場合は、どちらも最適なインターフェイスを返します。

これは、当初の仮定が不合理であったと言っていることです。つまり、そうでない場合FindInterfaceWith実装できませFindAssignableWithん。

4

4 に答える 4

10

これが私の実装です:

FindAssignableWithFindBaseClassWithおよびFindInterfaceWith実装

// provide common base class or implemented interface
public static Type FindAssignableWith(this Type typeLeft, Type typeRight)
{
    if(typeLeft == null || typeRight == null) return null;

    var commonBaseClass = typeLeft.FindBaseClassWith(typeRight) ?? typeof(object);

    return commonBaseClass.Equals(typeof(object))
            ? typeLeft.FindInterfaceWith(typeRight)
            : commonBaseClass;
}

// searching for common base class (either concrete or abstract)
public static Type FindBaseClassWith(this Type typeLeft, Type typeRight)
{
    if(typeLeft == null || typeRight == null) return null;

    return typeLeft
            .GetClassHierarchy()
            .Intersect(typeRight.GetClassHierarchy())
            .FirstOrDefault(type => !type.IsInterface);
}

// searching for common implemented interface
// it's possible for one class to implement multiple interfaces, 
// in this case return first common based interface
public static Type FindInterfaceWith(this Type typeLeft, Type typeRight)
{
    if(typeLeft == null || typeRight == null) return null;

    return typeLeft
            .GetInterfaceHierarchy()
            .Intersect(typeRight.GetInterfaceHierarchy())
            .FirstOrDefault();   
}

// iterate on interface hierarhy
public static IEnumerable<Type> GetInterfaceHierarchy(this Type type)
{
    if(type.IsInterface) return new [] { type }.AsEnumerable();

    return type
            .GetInterfaces()
            .OrderByDescending(current => current.GetInterfaces().Count())
            .AsEnumerable();
}

// interate on class hierarhy
public static IEnumerable<Type> GetClassHierarchy(this Type type)
{
    if(type == null) yield break;

    Type typeInHierarchy = type;

    do
    {
        yield return typeInHierarchy;
        typeInHierarchy = typeInHierarchy.BaseType;
    }
    while(typeInHierarchy != null && !typeInHierarchy.IsInterface);
}

FindInterfaceWith実施に関する注意

いずれかを実装している、または他のインターフェイスIEnumerableよりIEnumerable<T>も先に選択されるインターフェイス、これは正しくないと考えたもの

の自由形式の質問FindInterfaceWith

、次のサンプルではどのインターフェイスが望ましいを知る方法がないためFindInterfaceWith、最初のインターフェイスがによって返されます。IAIB

multiple_interfaces_implementing

インターフェイスとクラスの階層

    public interface IBase {}
    public interface ISomething {}
    public interface IDerivied: IBase {}
    public interface IDeriviedRight: IDerivied {}
    public interface IDeriviedLeft: IDerivied, IDisposable {}

    public class AnotherDisposable: IDisposable {
        public void Dispose() {
        }
    }

    public class DeriviedLeft: IDeriviedLeft {
        public void Dispose() {
        }
    }

    public class SubDeriviedLeft: DeriviedLeft {}
    public class SecondSubDeriviedLeft: DeriviedLeft {}
    public class ThirdSubDeriviedLeft: DeriviedLeft, ISomething {}

    public class Another {}
    public class DeriviedRight: IDeriviedRight {}

テストケース

NUnitそして、アサーションを使用した一連のテストケース:

FindBaseClassWithアサーションの例

// FindBaseClassWith returns null if one of parameters was an interface. 
// FindBaseClassWith  return null if any of parameter was null. 
Assert.That(typeof(DeriviedLeft).FindBaseClassWith(typeof(DeriviedLeft)), Is.EqualTo(typeof(DeriviedLeft)));

FindInterfaceWithアサーションの例

// FindInterfaceWith returns null if they don't have common implemented interface. 
// FindBaseClassWith  return null if any of parameter was null. 
Assert.That(typeof(DeriviedLeft).FindInterfaceWith(typeof(DeriviedLeft)), Is.EqualTo(typeof(IDeriviedLeft)));

FinAssignableWithアサーションの例

Assert.That(typeof(DeriviedLeft).FindAssignableWith(typeof(DeriviedLeft)), Is.SameAs(typeof(DeriviedLeft)));

CodeReviewでのディスカッション

この回答のレビューcodereview.stackexchange.com

ps
利用可能な完全なソース[ここ]

于 2013-01-03T11:01:43.763 に答える
2

ああ、そうだね、私は最近何か他のもののために書いたものを見せびらかすことができる!:)

警告:このコードは世界で最も効率的ではなく、コメントも非常に不十分です-個人的なプロジェクト用であり、私はすでにそれがどのように機能するかを知っていました-しかし、それはあなたが求めているものをあなたに与えると思います...

あなたが最も興味がある方法は public static Tuple<Type, IEnumerable<Type>> GetCommonBases(Type left, Type right)

返されるタプルは<共通基本クラス(共通インターフェースのリスト)>です。

簡単な要約:このクラスは、タイプが与えられると、次のことを行います。

  • Reverseは、指定されたタイプがベースタイプにヒットしなくなるまでウォークアップし、それぞれを「作業スタック」にプッシュします。

  • 各基本タイプを作業スタックからポップし、ツリーのような構造に挿入します。タイプが何らかのインターフェースを実装している場合、それらのインターフェースタイプのノードも追加します

  • ヘルパーメソッドは、最初のタイプに対してGetCommonBasesこれらのTypeTree構造の1つを作成し、次に他の指定されたタイプのタイプツリーに「マージ」します。これは、共通のベースタイプが存在するポイントが見つかるまで、共通のベースタイプをウォークダウンすることによって行われます。 2つのタイプ。その時点でツリーの2つのブランチが形成されます。次に、ルート(つまり、)から各タイプに「ドリルダウン」しSystem.Object、最初の偏差点を見つけます。この偏差点の親は、コモンベースタイプです。

  • インターフェイス部分は、の定義に依存しますInterfaces。これは、任意の祖先の任意のインターフェイスノードを「継承」します。このGetCommonBasesメソッドは、渡された2つの型によって実装されたインターフェイスのリストを取得し、これら2つのリストの共通部分を返します。つまり、両方の型で渡されたインターフェイスのセットが実装されます。

  • 次に、メソッドはこれらの2ビットの情報をとして返しますTuple<Type, IEnumerable<Type>>。最初の項目は共通の基本型(存在する場合)であり、2番目の項目は共通のインターフェースの共通部分です。


public class TypeTree
{
   private TypeTree()
   {
       Children = new List();
   }

   public TypeTree(Type value)
       : this()
   {
       // Get to the basest class
       var typeChain = GetTypeChain(value).ToList();
       Value = typeChain.First();
       foreach (var type in typeChain.Skip(1))
       {
           Add(type);
       }
   }

   public Type Value { get; private set; }
   public TypeTree Parent { get; private set; }
   public List Children { get; private set; }
   public IEnumerable Interfaces
   {
       get
       {
           var myInterfaces = Children.Where(c => c.Value.IsInterface);
           return Parent == null ? myInterfaces : myInterfaces.Concat(Parent.Interfaces).Distinct();
       }
   }

   public TypeTree Find(Type type)
   {
       if (Value == type)
           return this;
       return Children.Select(child => child.Find(type)).FirstOrDefault(found => found != null);
   }

   public TypeTree Add(Type type)
   {
       TypeTree retVal = null;
       if (type.IsInterface)
       {
           if (Value.GetInterfaces().Contains(type))
           {
               retVal = new TypeTree { Value = type, Parent = this };
               Children.Add(retVal);
               return retVal;
           }
       }
       var typeChain = GetTypeChain(type);
       var walkTypes =
           from baseType in typeChain
           let alreadyExists = Value == baseType || Children.Any(c => c.Value == baseType)
           where !alreadyExists
           select baseType;
       foreach (var baseType in walkTypes)
       {
           if (baseType.BaseType == Value)
           {
               // Add this as a child of the current tree
               retVal = new TypeTree { Value = baseType, Parent = this };
               Children.Add(retVal);
           }
           if (Value.IsAssignableFrom(baseType))
           {
               // we can add this as a child, potentially
               retVal = Children.Aggregate(retVal, (current, child) => child.Add(baseType) ?? current);
           }
           // add interfaces
           var interfaces = baseType.GetInterfaces().Where(i => i != type);
           foreach (var intType in interfaces)
           {
               (retVal ?? this).Add(intType);
           }
       }
       return retVal;
   }

   public override string ToString()
   {
       var childTypeNames = Children.Select(c => c.ToString()).Distinct();
       return string.Format("({0} {1})", Value.Name, string.Join(" ", childTypeNames));
   }

   public static Tuple> GetCommonBases(Type left, Type right)
   {
       var tree = new TypeTree(left);
       tree.Add(right);
       var findLeft = tree.Find(left);
       var findRight = tree.Find(right);

       var commonInterfaces =
           findLeft.Interfaces.Select(i => i.Value)
           .Intersect(findRight.Interfaces.Select(i => i.Value))
           .Distinct();

       var leftStack = new Stack();
       var temp = findLeft;
       while (temp != null)
       {
           leftStack.Push(temp);
           temp = temp.Parent;
       }
       var rightStack = new Stack();
       temp = findRight;
       while (temp != null)
       {
           rightStack.Push(temp);
           temp = temp.Parent;
       }
       var zippedPaths = leftStack.Zip(rightStack, Tuple.Create);
       var result = zippedPaths.TakeWhile(tup => tup.Item1.Value == tup.Item2.Value).Last();            
       return Tuple.Create(result.Item1.Value, commonInterfaces);
   }

   private static IEnumerable GetTypeChain(Type fromType)
   {
       var typeChain = new Stack();
       var temp = fromType;
       while (temp != null)
       {
           typeChain.Push(temp);
           temp = temp.BaseType;
       }
       return typeChain;
   }

}

于 2013-01-01T09:11:17.683 に答える
1

+1を更新:そして今、愚かな間違いといくつかの詳細なしで

私はこれがあなたが探しているものだと思います:

public static Type FindAssignableWith(this Type typeLeft, Type typeRight) {
    if(typeLeft==null||typeRight==null)
        return null;

    var typeLeftUion=typeLeft.GetInterfaceHierarchy().Union(typeLeft.GetClassHierarchy());
    var typeRightUion=typeRight.GetInterfaceHierarchy().Union(typeRight.GetClassHierarchy());

    return 
        typeLeftUion.Intersect(typeRightUion)
            .OrderByDescending(interfaceInHierarhy => interfaceInHierarhy.GetInterfaces().Contains(typeof(IEnumerable)))
            .ThenByDescending(interfaceInHierarhy => interfaceInHierarhy.Equals(typeof(IEnumerable)))
            .FirstOrDefault();
}

基本的に、基本クラスとインターフェイスを同じ順序で扱います。
基本的な実装は[ここ]からだと思います。
私がしたことは、元の機能のセマンティクスを変更せずに、基本的に2つのメソッドを結合することです。

例:

var result=typeof(char[]).FindAssignableWith2(typeof(string[]));
Console.WriteLine("{0}", typeof(char[]).FindAssignableWith2(typeof(string[]))); // IList
Console.WriteLine("{0}", typeof(Test).FindAssignableWith2(typeof(string[]))); // Object
// and so on...
于 2013-01-09T18:49:05.480 に答える
1

デフォルトの実装と、いくつかのよく知られたクラスとインターフェイスを優先度順に並べ替えておく必要があります。ここに私の実装:

private static List<Type> CommonTypesPriorities = new List<Type> 
                                       {
                                           typeof(IEnumerable), 
                                           typeof(Array), 
                                           typeof(IClonable)
                                       };

public static Type FindAssignableWith(this Type type1, Type type2)
{
    if(type1 == type2) 
        return type1;

    var baseClass = type1.FindBaseClassWith(type2);

    //if the base class is not object/null and it is not in the list, then return it.
    if(baseClass != typeof(object) && baseClass != null && !CommonTypesPriorities.Contains(type))
        return baseClass;

    var @interface = type1.FindInterfaceWith(type2);

    if(@interface == null)
        return baseClase;

    //if there's no base class and the found interface is not in the list, return it
    if(baseClass != null && !CommonTypesPriorities.Contains(@interface)                         
        return @interface;

    //Now we have some class and interfaces from the list.

    Type type = null;
    int currentPriority;

    //if the base class is in the list, then use it as the first choice
    if(baseClass != null && CommonTypesPriorities.Contains(type))
    {
        type = baseClass;
        currentPriority = CommonTypesPriorities.IndexOf(type);
    }

    var interfaces1 = type1.GetInterfaces();
    var interfaces2 = type2.GetInterfaces();

    foreach(var i in interfaces1)
    {
        if(interfaces2.Contains(i))
        {
            //We found a common interface. Let's check if it has more priority than the current one
            var priority = CommonTypesPriorities.IndexOf(i);
            if(i >= 0 && i < currentPriority)
            {
                currentPriority = priority;
                type = i;
            }
        }
    }

    return type;

}

それが役に立てば幸い。

于 2013-01-03T15:05:57.123 に答える