(Примечание: одно решение, которое работает, вы можете найти в нижней части этого вопроса)
В настоящее время я пытаюсь провести рефакторинг обобщенного c класса / интерфейса, где у меня есть ковариантный и контравариантный параметр. Причина рефакторинга заключается в том, что на данный момент я должен реализовать для всех приведенных вариантов использования класс N раз.
Моя цель / идея заключалась в том, чтобы написать абстрактный класс, содержащий разделяемые функциональные возможности, и сделать один сложный метод абстрактным, который нуждается в конкретной реализации для каждого варианта использования. Абстрактный базовый класс по унаследованным причинам все еще будет наследовать от исходного интерфейса, который является обобщенным c one.
. Вы можете найти исходную структуру кода здесь: https://dotnetfiddle.net/ZgGihl
То, что я пытался сделать, это следующий фрагмент кода
https://dotnetfiddle.net/Aub8Ov
С этим способом связаны две проблемы:
- Очевидным является то, что он не может скомпилироваться, потому что, если я правильно понял противоречивые варианты, он ожидает точный тип.
- Вторая проблема заключается в том, что я только что создал три отдельных экземпляра обобщенного c экземпляра абстрактного класса, которые не может использоваться в качестве общей ссылки для конкретной реализации.
Вопросы
- Есть ли способ каким-либо образом решить проблему с помощью обобщений и ковариантов без введения нового интерфейса?
interface test <out TInput, in TResult>
{
void setResult(TResult result);
TInput GetInput();
}
abstract class SemiConcreteTest<TInput, TResult> : test<TInput, TResult>
{
public TResult field;
public void setResult(TResult result)
{
field = result;
}
public abstract TInput GetInput();
}
//The UseCases
....
class concreteTest_A<TResult> : SemiConcreteTest<int,TResult>
{
public override int GetInput()
{
//Some information gathering
throw new System.NotImplementedException();
}
}
//UsesCase Specific Classes
interface Itag
{
}
class A : Itag{}
class B : Itag{}
class C : Itag{}
Этот путь все равно приведет к другой ссылке, как показано в примере. Первый параметр Generi c можно исключить, используя дженерики непосредственно для метода вместо класса, но это все равно оставит проблему, касающуюся контравариантного
public static void Main()
{
SemiConcreteTest<int, Itag> a = new concreteTest_A<A>();
SemiConcreteTest<short, Itag> b = new concreteTest_B<B>();
SemiConcreteTest<long, Itag> c = new concreteTest_C<C>();
}
Мое решение, исключающее контравариантный параметр
Что бы я сделал лично, это угробил бы контравариантный параметр alltogther и заменил его соответствующим интерфейсом, но коллега попросил меня *1047*, если возможно, во избежание этого .
Моим решением было бы следующее (https://dotnetfiddle.net/iql682):
interface DitchTheContraVariant{}
//The legacy Interface
interface ITest
{
void setResult(DitchTheContraVariant result);
TInput GetInput<TInput>();
}
Я решаю проблему, связанную с общей ссылкой, используя универсальный c метод. как видно выше в интерфейсе.
abstract class SemiConcreteTest : ITest {
DitchTheContraVariant result;
public void setResult(DitchTheContraVariant result){
this.result = result;
}
public abstract TInput GetInput<TInput>();
}
//The UseCases
class concreteTest_A : SemiConcreteTest
{
public override T GetInput<T>()
{
//Some information gathering
throw new System.NotImplementedException();
}
}
class A :DitchTheContraVariant {}
class B :DitchTheContraVariant {}
class C :DitchTheContraVariant {}
public static void Main()
{
SemiConcreteTest a = new concreteTest_A();
a = new concreteTest_B();
a = new concreteTest_C();
a.setResult(new A());
int b = a.GetInput<int>();
Console.WriteLine("Hello");
}
Но это привело бы к появлению нового интерфейса в базе кода, которого я стараюсь избегать, но я понятия не имею, как. Я не уверен, как продать мою версию моему коллеге.