#c# #generics #interface
#c# #общие #интерфейс
Вопрос:
У меня есть набор структур данных, который выглядит следующим образом:
- различные точки данных, которые реализуют
IDataPoint
интерфейс (для обеспечения существования значения и точки координат) - каждая точка данных может иметь разные типы координат (2D, 3D и т.д.)
- координаты хранятся в общих кортежах, которые все реализуют интерфейс
ITuple
(для обеспечения существования хотя бы одной (X) координаты)
Моя проблема в том, что я не выяснил, как создать Coord
свойство IDataPoint
универсального, чтобы координаты могли быть либо 2d, либо 3d (или что-то еще, если потребуется позже). Это моя попытка:
public interface IDataPoint
{
float Value { get; }
<Tuple> Coords { get; } where <ITuple> : ITuple
string ToString();
}
Где моя ошибка или это просто невозможно?
Остальная часть кода
public interface ITuple<T>
{
T X { get; }
string ToString();
}
public struct TwoTuple<T> : ITuple<T>
{
public T X { get; }
public T Y { get; }
public TwoTuple(T x, T y)
{
X = x;
Y = y;
}
public override string ToString()
{
return "(" X ", " Y ")";
}
}
public struct ThreeTuple<T> : ITuple<T>
{
public T X { get; }
public T Y { get; }
public T Z { get; }
public ThreeTuple(T x, T y, T z)
{
X = x;
Y = y;
Z = z;
}
public override string ToString()
{
return "(" X ", " Y ", " Z ")";
}
}
public interface IDataPoint
{
float Value { get; }
<Tuple> Coords { get; } where <ITuple> : ITuple
string ToString();
}
public struct BarDataPoint : IDataPoint
{
public TwoTuple<float> Coords { get; }
public float Value { get; }
public BarDataPoint(TwoTuple<float> Coords, float Value)
{
this.Coords = Coords;
this.Value = Value;
}
public override string ToString()
{
return "Coordinates: " Coords "; Value: " Value;
}
}
public struct ScatterDataPoint : IDataPoint
{
public ThreeTuple<float> Coords { get; }
public float Value { get; }
public ScatterDataPoint(ThreeTuple<float> coords, float value)
{
this.Coords = coords;
this.Value = value;
}
public override string ToString()
{
return "Coordinates: " Coords "; Value: " Value;
}
}
Комментарии:
1.
public interface IDataPoint<T> { ITuple<T> Coords { get; } }
?2. @JohnathanBarclay в нем говорится, что <T> отсутствует в пространстве имен. Если я добавлю его через
IDataPoint<T>
, это сработает, но затем реализация снова не сработает. Потому что, если я тогда скажуBarDataPoint : IDataPoint<float>
, что это выдает ошибку, потому что возвращаемый тип дляBarDataPoint
isTwoTuple
и notITuple
Ответ №1:
Интерфейс
public interface IDataPoint<TTuple>
{
float Value { get; }
TTuple Coords { get; }
string ToString();
}
Использование
public struct BarDataPoint : IDataPoint<ThreeTuple<float>>
{
public float Value { get; }
public ThreeTuple<float> Coords { get; set; }
public BarDataPoint(ThreeTuple<float> Coords, float Value)
{
this.Coords = Coords;
this.Value = Value;
}
public override string ToString()
{
return "Coordinates: " Coords "; Value: " Value;
}
}
Комментарии:
1. Это работает! Большое спасибо! Остался один вопрос: как мне теперь использовать это в функции?
public void ShowData<TDataPoint>(TDataPoint datapoint) where TDataPoint : IDataPoint<TwoTuple<float>>
работает, но я хотел бы сделатьTwoTuple
универсальный.2. При таком подходе вы отказываетесь от ограничения, которое тип
Coords
должен реализоватьITuple
интерфейс.3. @tadeseus Как насчет этого?
public static void ShowData<T>(IDataPoint<T> dataPoint) where T: ITuple<float> { //loqic }
4. Это означает, что точки данных 2D и 3D теперь являются совершенно разными типами, которые нельзя обрабатывать унифицированным способом.
5. @AdiletB это снова работает! Ты мой спаситель. Однако есть (конечно) последняя проблема. Классы, использующие эти типы данных, больше не работают..
public class ScatterPlot : Plot<ScatterDataPoint>
не работает, потому что «Нет преобразования бокса из’ScatterDataPoint’ в’IDataPoint'».
Ответ №2:
Сделайте это так, как вы делали это в своем ITuple
интерфейсе, указав общие элементы и их ограничения в объявлении интерфейса:
public interface IDataPoint<T,T2> where T : ITuple<T2>
{
float Value { get; }
T Coords { get; }
string ToString();
}
Coords
Теперь ваше свойство имеет тип T
и T
должно реализовывать интерфейс ITuple<T2>
. Ваш класс BarDataPoint
должен быть определен следующим образом:
public struct BarDataPoint : IDataPoint<TwoTuple<float>,float>
Вам придется дважды указать компилятору общий тип ITuple<T>
. Онлайн-демонстрация: https://dotnetfiddle.net/CYw1bR
Ответ №3:
Что вы можете сделать, так это:
public interface IDataPoint
{
float Value { get; }
ITuple Coords { get; }
string ToString();
}
вам не нужны обобщения. ITuple
является базовой реализацией общего назначения для кортежей.
все они являются производными от ITuple
System.Tuple<T1>
System.Tuple<T1,T2>
System.Tuple<T1,T2,T3>
System.Tuple<T1,T2,T3,T4>
System.Tuple<T1,T2,T3,T4,T5>
System.Tuple<T1,T2,T3,T4,T5,T6>
System.Tuple<T1,T2,T3,T4,T5,T6,T7>
System.Tuple<T1,T2,T3,T4,T5,T6,T7,TRest>
System.ValueTuple
System.ValueTuple<T1>
System.ValueTuple<T1,T2>
System.ValueTuple<T1,T2,T3>
System.ValueTuple<T1,T2,T3,T4>
System.ValueTuple<T1,T2,T3,T4,T5>
System.ValueTuple<T1,T2,T3,T4,T5,T6>
System.ValueTuple<T1,T2,T3,T4,T5,T6,T7>
System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,TRest>
Комментарии:
1.
ITuple
является универсальным.2. Это интерфейс. не универсальный
3.
ITuple
как определено OP, является общим.4. мой ITuple не совпадает с его ITuple. мой — это интерфейс
5. OPs
ITuple
также является интерфейсом. OPsITuple
также определяет свойствоX
, которое, как я полагаю, является обязательным.