アップデート:
FindInterfaceWith
インターフェースであるときに型自体を考慮に入れる限り、基本クラスは必ずしも関与しないため、単純化でき、フラット化された型階層を構築することは冗長になります。そこで、拡張メソッドを追加しましたGetInterfaces(bool)
。カバレッジのルールによってインターフェイスを並べ替えることができるため、並べ替えられたインターフェイスの交差が候補になります。それらのすべてが等しく良い場合、私はそれらのどれもが最良のものとは見なされないと言いました。そうでない場合は、最良のものが他の1つをカバーする必要があります。そして、それらはソートされているため、この種の関係は、配列内の右端の2つのインターフェースに存在して、最も具体的な共通の最良のインターフェースがあることを示す必要があります。
Linq
;を使用すると、コードを簡略化できます。しかし、私のシナリオでは、参照と名前空間の要件を可能な限り減らす必要があります。
コード
using System;
public static class TypeExtensions {
static int CountOverlapped<T>(T[] ax, T[] ay) {
return IntersectPreserveOrder(ay, ax).Length;
}
static int CountOccurrence(Type[] ax, Type ty) {
var a = Array.FindAll(ax, x => Array.Exists(x.GetInterfaces(), tx => tx.Equals(ty)));
return a.Length;
}
static Comparison<Type> GetCoverageComparison(Type[] az) {
return (tx, ty) => {
int overlapped, occurrence;
var ay = ty.GetInterfaces();
var ax = tx.GetInterfaces();
if(0!=(overlapped=CountOverlapped(az, ax).CompareTo(CountOverlapped(az, ay)))) {
return overlapped;
}
if(0!=(occurrence=CountOccurrence(az, tx).CompareTo(CountOccurrence(az, ty)))) {
return occurrence;
}
return 0;
};
}
static T[] IntersectPreserveOrder<T>(T[] ax, T[] ay) {
return Array.FindAll(ax, x => Array.FindIndex(ay, y => y.Equals(x))>=0);
}
/*
static T[] SubtractPreserveOrder<T>(T[] ax, T[] ay) {
return Array.FindAll(ax, x => Array.FindIndex(ay, y => y.Equals(x))<0);
}
static Type[] GetTypesArray(Type typeNode) {
if(null==typeNode) {
return Type.EmptyTypes;
}
var baseArray = GetTypesArray(typeNode.BaseType);
var interfaces = SubtractPreserveOrder(typeNode.GetInterfaces(), baseArray);
var index = interfaces.Length+baseArray.Length;
var typeArray = new Type[1+index];
typeArray[index]=typeNode;
Array.Sort(interfaces, GetCoverageComparison(interfaces));
Array.Copy(interfaces, 0, typeArray, index-interfaces.Length, interfaces.Length);
Array.Copy(baseArray, typeArray, baseArray.Length);
return typeArray;
}
*/
public static Type[] GetInterfaces(this Type x, bool includeThis) {
var a = x.GetInterfaces();
if(includeThis&&x.IsInterface) {
Array.Resize(ref a, 1+a.Length);
a[a.Length-1]=x;
}
return a;
}
public static Type FindInterfaceWith(this Type type1, Type type2) {
var ay = type2.GetInterfaces(true);
var ax = type1.GetInterfaces(true);
var types = IntersectPreserveOrder(ax, ay);
if(types.Length<1) {
return null;
}
Array.Sort(types, GetCoverageComparison(types));
var type3 = types[types.Length-1];
if(types.Length<2) {
return type3;
}
var type4 = types[types.Length-2];
return Array.Exists(type3.GetInterfaces(), x => x.Equals(type4)) ? type3 : null;
}
public static Type FindBaseClassWith(this Type type1, Type type2) {
if(null==type1) {
return type2;
}
if(null==type2) {
return type1;
}
for(var type4 = type2; null!=type4; type4=type4.BaseType) {
for(var type3 = type1; null!=type3; type3=type3.BaseType) {
if(type4==type3) {
return type4;
}
}
}
return null;
}
public static Type FindAssignableWith(this Type type1, Type type2) {
var baseClass = type2.FindBaseClassWith(type1);
if(null==baseClass||typeof(object)==baseClass) {
var @interface = type2.FindInterfaceWith(type1);
if(null!=@interface) {
return @interface;
}
}
return baseClass;
}
}
2つの再帰的な方法があります。1つはFindInterfaceWith
、もう1つは重要なメソッドです。これは、使用法が異なるクラスGetTypesArray
という名前のメソッドがすでに存在するためです。GetTypeArray
Type
これは、 AkimがGetClassHierarchyを提供したメソッドのように機能します。ただし、このバージョンでは、次のような配列を作成します。
私たちが知っているように、それらは特定の順序であり、それが物事を機能させる方法です。構築されたアレイGetTypesArray
は、実際にはフラット化されたツリーです。配列は実際には次のようにモデルに含まれています。
ダイアグラム
IList<int>
実装などの一部のインターフェイス実装の関係はICollection<int>
、この図の線とリンクされていないことに注意してください。
返される配列のインターフェイスは、によってArray.Sort
提供される順序付けルールで並べ替えられGetCoverageComparison
ます。
言及すべきことがいくつかあります。たとえば、複数のインターフェースの実装の可能性は、いくつかの回答([ this ]など)で一度だけではなく言及されています。そして私はそれらを解決する方法を定義しました、それらは次のとおりです:
ノート
GetInterfacesメソッドは、アルファベット順や宣言順など、特定の順序でインターフェイスを返しません。コードは、インターフェースが返される順序に依存してはなりません。その順序は異なるためです。
再帰のため、基本クラスは常に順序付けられます。
2つのインターフェースのカバレッジが同じである場合、どちらも適格とは見なされません。
これらのインターフェースが定義されていると仮定します(またはクラスは問題ありません)。
public interface IDelta {
}
public interface ICharlie {
}
public interface IBravo: IDelta, ICharlie {
}
public interface IAlpha: IDelta, ICharlie {
}
IAlpha
次に、との割り当てにどちらが適していIBravo
ますか?この場合、FindInterfaceWith
を返すだけnull
です。
質問[ 2つのタイプ(重複)の中で最小の割り当て可能なタイプを見つける方法は?]、私は述べました:
間違った控除
この仮定が正しければ、FindInterfaceWith
は冗長な方法になります。FindInterfaceWith
との唯一の違いFindAssignableWith
は次のとおりです。
FindInterfaceWith
null
クラスの最良の選択があった場合に戻ります。whileFindAssignableWith
は、正確なクラスを直接返します。
ただし、メソッドを確認できるようになりました。FindAssignableWith
他の2つのメソッドを呼び出す必要があります。これは、元の仮定に基づいています。逆説的なバグは魔法のように消えました。
インターフェイスの順序付けのカバレッジ比較ルールについて、デリゲートGetCoverageComparison
では、次を使用します。
デュアルルール
ソースインターフェイス配列内の2つのインターフェイスを比較します。それぞれが、ソース内の他のインターフェイスの数をカバーしています。CountOverlapped
ルール1がそれらを区別しない場合(returns 0
)、2番目の順序は、呼び出しCountOccurrence
て、どれが他のユーザーにさらに何度も継承されているかを判別してから比較することです。
Linq
2つのルールはクエリと同等です。
interfaces=(
from it in interfaces
let order1=it.GetInterfaces().Intersect(interfaces).Count()
let order2=(
from x in interfaces
where x.GetInterfaces().Contains(it)
select x
).Count()
orderby order1, order2
select it
).ToArray();
FindInterfaceWith
次に、再帰呼び出しを実行して、このインターフェイスが最も一般的なインターフェイスとして認識されるのに十分であるか、またはのような別の関係であるかを判断IAlpha
しますIBravo
。
また、メソッドについては、FindBaseClassWith
返されるものは、パラメーターがnullの場合はnullを返すという元の仮定とは異なります。実際には、渡された別の引数を返します。
これは、「メソッド `FindBaseClassWith`は何を返す必要がありますか? ]という質問に関連しています。]のメソッドチェーンについてFindBaseClassWith
。現在の実装では、次のように呼び出すことができます。
メソッドチェーン
var type=
typeof(int[])
.FindBaseClassWith(null)
.FindBaseClassWith(null)
.FindBaseClassWith(typeof(char[]));
戻りtypeof(Array)
ます; この機能のおかげで、私たちは呼び出すことさえできます
var type=
typeof(String)
.FindAssignableWith(null)
.FindAssignableWith(null)
.FindAssignableWith(typeof(String));
FindInterfaceWith
とのような関係の可能性があるため、私の実装ではできない可能性があるのは、上記のように呼び出すIAlpha
ことIBravo
です。
FindAssignableWith
示されている例のように呼び出すことによって、いくつかの状況でコードをテストしました。
割り当て可能なタイプの出力
(Dictionary`2, Dictionary`2) = Dictionary`2
(List`1, List`1) = IList
(Dictionary`2, KeyValuePair`2) = Object
(IAlpha, IBravo) = <null>
(IBravo, IAlpha) = <null>
(ICollection, IList) = ICollection
(IList, ICollection) = ICollection
(Char[], Int32[]) = IList
(Int32[], Char[]) = IList
(IEnumerable`1, IEnumerable`1) = IEnumerable
(String, Array) = Object
(Array, String) = Object
(Char[], Int32[]) = IList
(Form, SplitContainer) = ContainerControl
(SplitContainer, Form) = ContainerControl
List'1
テストが表示されるのは、 ;IList
でテストtypeof(List<int>)
したためです。typeof(List<String>)
とDictionary'2
は両方Dictionary<String, String>
です。正確な型名を提示する作業をしなかったことをお詫びします。