#c# #generics #interface #covariance
#c# #обобщения #интерфейс #ковариация
Вопрос:
У меня есть интерфейс, который преобразует сетку данных, которую я хочу реализовать:
public interface ITransformable<T>
{
T Slice(int x, int y, int width, int height);
T Slice(Rectangle value);
T Transpose();
T Flip(bool horizontal, bool vertical);
T Rotate(bool clockwise);
}
Итак, я создаю класс, который делает это:
public class Mesh<T> : ITransformable<Mesh<T>>
{
public Mesh<T> Transpose() { ... }
/// etc
}
Однако при создании более производной версии mesh я сталкиваюсь с проблемой. Например, у меня есть Heightmap
класс, который является Mesh<float>
. Создавая конкретную реализацию T, это позволяет мне использовать перегрузки операторов, чтобы я мог легко добавлять, например, две карты высот вместе. Но когда я его реализую
public class Heightmap : Mesh<float> { ... }
Heightmap
функции из ITransformable по-прежнему возвращают сетку, а не карту высот. Есть ли какой-либо способ реализовать базовое поведение в Mesh
, но «изменить» возвращаемый тип в более производном классе? Я думал, что это цель ковариации, но, похоже, я не могу понять это.
Комментарии:
1. Если вы собираетесь переопределить
Transpose
, вам нужно сделать этоvirtual
вMesh<T>
, затемoverride Heightmap Slice(int x, int y, int width, int height) {...}
внутриHeightmap
. Если вы уже пробовали это, пожалуйста, покажите сообщение об ошибке или то, как это не работает в вашем вопросе.2. Возможно, вам нужно передать способ создания производных объектов в базовый класс
Mesh<T,TImpl> : ITransformable<TImpl> where TImpl:Mesh<T>, new()
3. Несмотря на то, что интерфейс требует возврата a
Mesh<T>
, реализация все равно может возвращать aHeightmap
из переопределенной версииTranspose
(хотя это необходимо отметитьvirtual
). Вызывающий объект должен будет выполнить приведение кHeightmap
, если ему нужен доступ к определенным свойствам / методам этого класса.4. Похоже, у @luqui был ответ, который я искал. Просто переопределяя метод и возвращая
base.Transpose()
корректное изменение ввода, которое я хотел. Я чувствую себя глупо, спрашивая сейчас, но, по крайней мере, это работает!5. Кайл Баран: похоже, то, что вы просили, не соответствует тому, что вы хотели … или, может быть, в вашем комментарии используются неправильные термины — вы не можете переопределить метод и изменить возвращаемый тип… В любом случае — рассмотрите возможность публикации ответа.
Ответ №1:
Пожалуйста, попробуйте следующий код, он должен работать как шарм. Замените методы / исключения на вашу собственную реализацию.
public interface ITransformable<T, out S> where S : ITransformable<T, S>
{
S Slice(int x, int y, int width, int height);
S Slice(Rectangle value);
S Transpose();
S Flip(bool horizontal, bool vertical);
S Rotate(bool clockwise);
}
public class Mesh<T, S> : ITransformable<T, S> where S : Mesh<T,S>, ITransformable<T, S>, new()
{
public S Slice(int x, int y, int width, int height)
{
throw new NotImplementedException();
}
public S Slice(Rectangle value)
{
throw new NotImplementedException();
}
public S Transpose()
{
//The following will work smoothly.
S sObject = this.Slice(10, 20, 30, 40);
return sObject;
}
public S Flip(bool horizontal, bool vertical)
{
throw new NotImplementedException();
}
public S Rotate(bool clockwise)
{
throw new NotImplementedException();
}
}
public class Heightmap : Mesh<float, Heightmap>
{
}
public class Program
{
static void Main()
{
Heightmap heightmap = new Heightmap();
Heightmap map2 = heightmap.Transpose(); //This will work smoothly.
}
}