Dies ist eine Erweiterung von this question, die eine Antwort hatte, die in diesem speziellen Fall funktioniert.Typ Inferenz zwischen Generika mit umgekehrten Einschränkungen
Meine eigentliche Code sieht mehr wie folgt aus:
public abstract class BaseComparable<TLeft, TRight>
{ }
public class LeftComparable<TLeft, TRight> : BaseComparable<TLeft, TRight> where TLeft : IComparable<TRight>
{
public LeftComparable(TLeft value) { }
}
public class RightComparable<TLeft, TRight> : BaseComparable<TLeft, TRight> where TRight : IComparable<TLeft>
{
public RightComparable(TLeft value) { }
}
Wenn Sie die äquivalente Reflexion Code zu verwenden, was ich geschrieben, es funktioniert super:
public static BaseComparable<TLeft, TRight> AsComparableFor<TLeft, TRight>(this TLeft left, TRight right)
{
if (left is IComparable<TRight>)
{
var constructor =
typeof(LeftComparable<,>).MakeGenericType(typeof(TLeft), typeof(TRight))
.GetConstructor(new[] { typeof(TLeft) });
if (constructor != null)
{
return (BaseComparable<TLeft, TRight>)constructor.Invoke(new object[] { left });
}
}
if (right is IComparable<TLeft>)
{
var constructor =
typeof(RightComparable<,>).MakeGenericType(typeof(TLeft), typeof(TRight))
.GetConstructor(new[] { typeof(TLeft) });
if (constructor != null)
{
return (BaseComparable<TLeft, TRight>)constructor.Invoke(new object[] { left });
}
}
throw new ArgumentException();
}
Dann können Sie
sagenclass Baz
{
public int Value { get; set; }
}
class Bar : IComparable<Baz>
{
public int Value { get; set; }
int IComparable<Baz>.CompareTo(Baz other)
{
return Value.CompareTo(other.Value);
}
}
// ....
var bar = new Bar { Value = 1 };
var baz = new Baz { Value = 1 };
var compBaz = baz.AsComparableFor(bar);
var compBar = bar.AsComparableFor(baz);
Fantastisch, Typ Inferenz funktioniert genau wie erwartet.
Die Anpassung der akzeptierte Antwort oben, jedoch
public static class Comparable
{
public static BaseComparable<TLeft, TRight>
AsComparableFor<TLeft, TRight>(this IComparable<TRight> left, TRight right)
where TLeft : IComparable<TRight>
{
if (left is TLeft)
{
if (left is IComparable<TRight>)
{
return new LeftComparable<TLeft, TRight>((TLeft)left);
}
}
throw new InvalidCastException();
}
public static BaseComparable<TLeft, TRight>
AsComparableFor<TLeft, TRight>(this TLeft left, IComparable<TLeft> right)
where TRight : IComparable<TLeft>
{
if (left is TLeft)
{
if (right is IComparable<TLeft>)
{
return new RightComparable<TLeft, TRight>((TLeft)left);
}
}
throw new InvalidCastException();
}
}
Sie erfordert die Typargumente explizit angeben:
//bar.AsComparableFor(baz);
//baz.AsComparableFor(bar); //Does not compile
bar.AsComparableFor<Bar, Baz>(baz);
baz.AsComparableFor<Baz, Bar>(bar); // Does compile
Ein großer Teil davon war die Bibliothek so schmerzlos zu machen, wie möglich, und ich habe das Gefühl, dass ich die Art der Niederlagen etwas spezifizieren muss.
Gibt es einen Mittelweg? Kann ich den saubereren, reflexionsfreien Code von der akzeptierten Antwort mit der Typrückschlussstärke des Originals erhalten?
Edit: full code can be found in this gist.
Wie können Sie zum Kompilieren die zu vergleichenden Eigenschaften ("int Value") kennen, wissen aber nur zur Laufzeit, welche Klassen Sie vergleichen werden? Dies scheint wie eine Trennung. Es scheint, als könnten Sie Ihre vergleichbare Klasse im Voraus konstruieren, anstatt ihre Informationen zur Laufzeit einzufügen. Plus, diese Art der Überprüfung zu tun ist hässlich. Vielleicht ist das nicht vermeidbar, aber yikes. Ich wünschte, du könntest das noch konkreter machen. Warum die Notwendigkeit, unterschiedliche Klassen zu vergleichen? – ErikE
@ErikE '[Left | Right] Comparable' und' Comparable' sind Bibliotheksklassen. 'Bar' und' Baz' sind nur Beispiele dafür, was Benutzercode wäre. – RoadieRich