Hi,
hat schon mal wer eine funktionierende Covariance LinkedList gebaut?
So etwas sollte funktionieren:
Node<BaseData> n1 = new Node<BaseData>();
Node<ExtendedData> n2 = new Node<ExtendedData>();
n1.Left = n2;
Toll wäre wenn die implementierende Klasse von INode das Ganze so verpackt, dass man da jetzt nicht mit 2-3 generischen Typen arbeiten muss.
Ich habe es mal so probiert. Wahrscheinlich nicht jedermanns Geschma#ck.
public interface INode<in TData, out TOutData>
{
INode<TData, TOutData> Left { get; }
INode<TData, TOutData> Right { get; }
TOutData Data { get; }
void SetLeft(TData data);
void SetRight(TData data);
void SetData(TData data);
}
Unschön am dem Konstrukt ist, dass SetLeft TData als Parameter erwartet und nicht den Typ INode<TData, TOutData>. Das heißt in der Implementierung von SetLeft und SetRight müsste ich eine neue Instanz mit INode<in TData, out TOutData> erzeugen. Normalerweise möchte man ja als Anwender der Node Klasse Left und Right zuweisen (siehe oben). Die Lösung ist somit nicht zufriedenstellend.
Evtl. könnte man das über einen dritten generischen Typ lösen. Hat wer andere Lösungsvorschläge?
Meinst du so etwas?
class Program
{
static void Main(string[] args)
{
var n1 = new Node<BaseData>();
var n2 = new Node<ExtendedData>();
n1.Left = n1;
}
}
interface INode<out TData>
{
TData Data { get; }
INode<TData> Left { get; }
INode<TData> Right { get; }
}
class Node<TData> : INode<TData>
{
public TData Data { get; set; }
public INode<TData> Left { get; set; }
public INode<TData> Right { get; set; }
}
class BaseData
{
}
class ExtendedData : BaseData
{
}
Hi Sir Rufo,
Danke für den Beitrag. Ich habe mir erlaubt deinen Code zu erweitern.
Mit der Lösung von dir muss man zum Setzen immer die Klasse Node verwenden. Wenn man nur mit dem Interface arbeitet, hat man keine Möglichkeit Left und Right zu setzen. Das hätte ich bei meinem Beispiel auch angeben sollen.
Ich hätte jetzt folgendes vorgeschlagen:
public interface INodeG<out TData>
{
TData Data { get; }
INodeG<TData> Left { get; }
INodeG<TData> Right { get; }
}
public interface INodeS<in TData>
{
void SetData(TData data);
void SetLeft(INodeG<TData> data);
void SetRight(INodeG<TData> data);
}
public class Node<TData> : INodeG<TData>, INodeS<TData>
{
protected TData _Data;
public TData Data { get => _Data; set => SetData(value); }
protected INodeG<TData> _Left;
public INodeG<TData> Left { get => _Left; set => SetLeft(value); }
protected INodeG<TData> _Right;
public INodeG<TData> Right { get => _Right; set => SetRight(value); }
public void SetData(TData data) => _Data = data;
public void SetLeft(INodeG<TData> data) => _Left = data;
public void SetRight(INodeG<TData> data) => _Right = data;
}
class Program
{
static void Main(string[] args)
{
INodeS<BaseData> n1 = new Node<BaseData>();
INodeG<ExtendedData> n2 = new Node<ExtendedData>();
n1.SetLeft(n2);
}
}
Imho gefällt mir nicht, dass man immer je nach dem, ob man Left oder Right setzen oder holen will, das passende Interface INodeS oder INodeG verwenden muss. Ein Interface dafür wäre händelbarer.
Hier mal ein Beispiel (mit anderen Bezeichnungen) und einer Auflistung, was geht und was nicht
class Program
{
static void Main(string[] args)
{
INode<BaseData> nBase = new Node<BaseData>();
INode<ExtendedData> nExtended = new Node<ExtendedData>();
IReadOnlyNode<BaseData> e1 = nExtended; // ok
IReadOnlyNode<ExtendedData> e2 = nExtended; // ok
IWriteableNode<ExtendedData> e3 = nExtended; // ok
IWriteableNode<BaseData> e4 = nExtended; // downgrade geht nicht!
IReadOnlyNode<BaseData> b1 = nBase; // ok;
IReadOnlyNode<ExtendedData> b2 = nBase; // upgrade geht nicht!
IWriteableNode<BaseData> b3 = nBase; // ok
IWriteableNode<ExtendedData> b4 = nBase; // ok
}
}
interface IReadOnlyNode<out TData>
{
TData Data { get; }
IReadOnlyNode<TData> Left { get; }
IReadOnlyNode<TData> Right { get; }
}
interface IWriteableNode<in TData>
{
TData Data { set; }
IReadOnlyNode<TData> Left { set; }
IReadOnlyNode<TData> Right { set; }
}
interface INode<TData> : IReadOnlyNode<TData>, IWriteableNode<TData>
{
new TData Data { get; set; }
new IReadOnlyNode<TData> Left { get; set; }
new IReadOnlyNode<TData> Right { get; set; }
}
class Node<TData> : INode<TData>
{
public TData Data { get; set; }
public IReadOnlyNode<TData> Left { get; set; }
public IReadOnlyNode<TData> Right { get; set; }
}
class BaseData
{
}
class ExtendedData : BaseData
{
}
Danke dir!
Eigentlich sollte nichts dagegen sprechen, wenn man aus der Node class ein Node struct macht? Left und Right sind bereits reference values - somit sollte sich an der "Bedingung" von den Nodes sich nichts ändern.
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
INode<BaseData> n1 = new Node<BaseData>();
n1.Data = new ExtendedData();
INode<BaseData> n2 = new Node<BaseData>();
n2.Data = new BaseData();
INode<BaseData> n3 = new Node<BaseData>();
n3.Data = new ExtendedData();
INode<BaseData> n4 = new Node<BaseData>();
INode<ExtendedData> n5 = new Node<ExtendedData>();
INode<ExtendedData> n6 = new Node<ExtendedData>();
n1.Left = n2;
n2.Left = n3;
n3.Left = n4;
n4.Left = n5;
n5.Left = n6;
INode<BaseData> current = n1;
do
{
Console.WriteLine(current.GetType());
current = current.Left as INode<BaseData>;
} while (current != null);
}
Wäre eine typensichere Realisierung von untenstehendem Beispiel möglich?
Node<Base>.Left => Node<Extended>.Left => Node<Base>.Left => Node<Extended>.Left ...