2015-08-20 2 views
0

Прошу прощения за этот довольно фундаментальный вопрос, однако я не могу найти документацию. Возможно, потому, что я не знаю правильной терминологии.C# Factory для общего наследования

структура Класс:

class D{} 

abstract class A<T>{} 
class B<T> : A<T> {} 
class C : B<D> {} 

Я пытаюсь создать метод фабрики возвращающегося C, в то время как общий тип возвращаемого значения должен быть A. К сожалению, эта реализация приведет к ошибке compiletime, Altough структура наследования, как представляется, виртуозно ковариантны.

public A<T> FactoryMethod() 
{ 
    return new C(); 
} 

Более конкретно, я пытаюсь реализовать завод, способный производить все три класса на основе входных значений, в то время как общая способность должна быть получена.

public A<T> FactoryMethod(int i, Type K) 
{ 
    if(i == 1) 
     return new A<K>(): 

    if(i == 2) 
     return new B<K>(): 

    if(i == 3) 
     return new C(): 
} 

Update

Я должен создать три объекта следующим образом.

A<string> first = FactoryMethod(1, string); 
A<int> second = FactoryMethod(2, int); 
A<int> third = FactoryMethod(3, int); 
+3

Какая ошибка у вас? Что такое 'T'? – SLaks

+0

T - это обычная типовая буква. Однако D - это конкретный класс. – Nex

+0

Что говорит ошибка компиляции? Вы не можете просто поставить 'T' там, если это не означает что-то в этой области. – 31eee384

ответ

2

C является подклассом связанного типа A<D>. Таким образом, справедливо следующее:

public A<D> FactoryMethod() 
{ 
    return new C(); 
} 

, так как мы можем с уверенностью сказать, что C является A<D>. Однако C не является общим типом и не может быть преобразован в открытый тип A<T>, где T является общим аргументом, поскольку T может быть любого типа.

Однако справедливо следующее:

public A<T> FactoryMethod() 
{ 
    return new B<T>(); 
} 

Поскольку B<T> является открытым универсальным типом, а также и любой B<T> является A<T>.


На основе вашего обновления, вы можете написать свой фабричный метод, как:

public A<T> FactoryMethod<T>(int i) 
{ 
    if(i == 1) 
     return new A<T>(): 
    if(i == 2) 
     return new B<T>(): 
    if(i == 3) 
     return (A<T>)(object)new C(): 
     // The cast to object gets rid of compile time checking, 
     // but will throw an InvalidCastExceptoin if T is not D 
} 

Это немного некрасиво с этим странным хака для случая 3. Тогда вы могли бы назвать его как:

A<string> first = FactoryMethod<string>(1); 
A<int> second = FactoryMethod<int>(2); 
A<int> third = FactoryMethod<int>(3); // InvalidCastException! 
+0

Есть ли еще один способ создать единый заводский метод, способный вернуть все три класса без необходимости указания T? – Nex

+0

Нет, не напрямую. Как вы могли бы создать 'A <>' или 'B <>' без указания 'T'. – shf301

+0

Я обновил вопрос. Я задаю T, хотя и не в обратном типе самого метода. – Nex

2

В рамках класса, содержащего FactoryMethod, T не имеет смысла. Вам нужно указать общий параметр A, чтобы использовать его как тип.

В этом случае, поскольку C является B<D> и B<D> является A<D>, то вы должны использовать A<D> в качестве возвращаемого типа.

Для вашего второго вопроса, если вы используете общий метод фабрики, я думаю, вы можете получить то, что хотите.

public class A<T> {} 

public class B<T> : A<T> {} 

public class C : B<D> {} 

public class D {} 

public class Test 
{ 
    public static A<T> FactoryMethod<T>(int i) 
    { 
     if(i == 1) 
      return new A<T>(); 
     if(i == 2) 
      return new B<T>(); 
     if(i == 3) 
      return (A<T>)(object)new C(); 
     return null; 
    } 

    public static void Main() 
    { 
     A<string> first = FactoryMethod<string>(1); 
     A<int> second = FactoryMethod<int>(2); 
     A<D> third = FactoryMethod<D>(3); 
    } 
} 

Поскольку C (A<D>) не может быть назначен на некоторые A<T> (компилятор не знает, что вы всегда будете проходить 3 когда T является D) это не типобезопасным.

+0

Есть ли еще один способ создать один заводский метод, способный вернуть все три класса без необходимости указания T? – Nex

+0

Я делал это в прошлом, имея интерфейс 'IA', так что' A : IA' и 'IA' предоставляют версии методов, которые работают без статического' T'. Но тогда вы начинаете терять безопасность по типу, и это компромиссная игра. – 31eee384

+0

К сожалению, большинство классов, прошедших как тип заводского метода, будут запечатаны (например, Bitmap) и не будут расширяться. – Nex

1

Учитывая это:

class D{} 

class A<T>{} 
class B<T> : A<T> {} 
class C : B<D> {} 
enum openT 
{ 
    level1, level2 
} 

Я думаю, что вы могли бы искать для этого:

public A<T> FactoryMethod<T>(openT i) 
{ 
    if(i == openT.level1) 
     return new A<T>(): 

    if(i == openT.level2) 
     return new B<T>(): 

} 

public A<D> FactoryMethod() 
{ 
    return new C(): 
} 

public static void Main() 
{ 
    A<string> first = OpenFactoryMethod<string>(1); 
    A<int> second = OpenFactoryMethod<int>(2); 
    A<D> third = FactoryMethod(); 
} 

Обратите внимание, что не может быть абстрактным, так как вы пытаетесь построить его.

Я не понимаю, что вы на самом деле пытаетесь сделать здесь, поскольку C является закрытым типом, и поэтому ваш заводский метод никогда не будет иметь для этого смысла.

ОБНОВЛЕНО

Следующая может быть ближе к тому, что вы ищете:

public TAofT FactoryMethod<TAofT, T>() where TAofT : A<T>, new() 
{ 
    return new TAofT(): 
} 

public static void Main() 
{ 
    A<string> first = FactoryMethod<A<string>, string>(); 
    A<int> second = FactoryMethod<B<int>, int>(); 
    A<D> third = FactoryMethod<C, D>(); 
} 

Но фабричный метод, то кажется излишним, так как вы можете просто сделать:

public static void Main() 
{ 
    A<string> first = new A<string>(); 
    A<int> second = new B<int>(); 
    A<D> third = new C(); 
} 

ОБНОВЛЕНИЕ 2

Если то, что вы действительно хотите, не так:

public abstract class AEnum<T, T3> where T3 : B<T>, new() 
{ 
    private static Func<A<T>> factoryMethod; 

    public static readonly Level1 = new AEnum<T>(()=>new A<T>()); 
    public static readonly Level2 = new AEnum<T>(()=>new B<T>()); 
    public static readonly Level3 = new AEnum<T>(()=>new T3()); 

    protected AEnum(Func<A<T>> factoryMethod) { this.factoryMethod = factoryMethod; } 

    public A<T> New() { return this.factoryMethod(); } 
} 

используется так:

public class DEnum : AEnum<D, C> 
{ 
} 

с:

public static void Main() 
{ 
    A<D> first = DEnum.Level1.New(); 
    A<D> second = DEnum.Level2.New(); 
    A<D> third = DEnum.Level3.New(); 
} 

Но тогда вы не можете смешивать типы перечислений, так как выше имеет тип, ограниченный до D.

Или вы могли бы сделать:

public class OpenAEnum<T, T3> : AEnum<T, T3> where T3 : B<T3> 
{ 
} 

public class CInt : B<int> {} 
public class Cstring : B<string> {} 

с:

public static void Main() 
{ 
    A<string> first = OpenAEnum<string, CString>.Level1.New(); 
    A<int> second = OpenAEnum<int, CInt>.Level2.New(); 
    A<D> third = OpenAEnum<D, C>.Level3.New(); 
} 

Что это такое, что вы пытаетесь сделать?

Смежные вопросы