2012-06-13 4 views
4

Есть ли способ определить абстрактный тип как параметр в абстрактном методе, а когда этот метод реализован в производном классе, вы измените тип метода для принятия производного тип?Изменение типа параметра при реализации абстрактного метода

Код:

public abstract class ProductBase 
{ 
} 

public class SomeProduct 
    : ProductBase 
{ 

} 

public abstract class A 
{ 
    protected abstract void addProduct(ProductBase p); 
} 

// This works 
public class B : A 
{   
    protected override void addProduct(ProductBase p) 
    { 
     // Do some work 
    } 
} 

// This is what I'd like to do 
public class C : A 
{ 
    // Compiler error here because I have accepted a SomeProduct and not a ProductBase 
    protected override void addProduct(SomeProduct p) 
    { 
     // Do some work on the specialisation - I can use the SomeProduct directly 
    } 
} 

В моей голове, это делает какой-то смысл. Абстрактный класс, указывающий на то, что существует метод, который производные классы должны реализовывать, но они могут изменять тип объекта, переданного в качестве параметра, при условии, что он принадлежит к одной цепочке наследования ...

Что я в итоге do, заключается в том, чтобы удалить абстрактный метод AddProduct из абстрактного класса, а вместо этого просто реализовать его в производном классе в любом случае, но в будущем нет контрактов для других классов, которые они должны создать свою собственную реализацию AddProduct. И это не чувствовать себя правильно.

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

Спасибо,
bgs264

ответ

6

Вы можете сделать свой A класс родовое и использовать общий аргумент в методе addProduct:

public abstract class A<TProduct> where TProduct : ProductBase 
{ 
    protected abstract void addProduct(TProduct p); 
} 

public class B : A<ProductBase> 
{   
    protected override void addProduct(ProductBase p) 
    { 
    } 
} 

public class C : A<SomeProduct> 
{ 
    protected override void addProduct(SomeProduct p) 
    { 
    } 
} 
1

Нет, это не представляется возможным: Предположим, вы вызываете addProduct на переменную класса A, что указывает на экземпляр класса C, передавая объект, который является ProductBase, но не a SomeProduct. Прошедший экземпляр не может быть преобразован в SomeProduct, поэтому он не будет компилироваться в первую очередь.

Ближе всего к этому является решение с обобщениями:

public abstract class A<T> 
    where T : ProductBase 
{ 
    protected abstract void addProduct(T p); 

} 

Затем вы можете определить класс C следующим образом:

public class C : A<SomeProduct> 
{ 
    protected override void addProduct(SomeProduct p); 
} 

Конечно, это означает, что вы должны ввести любой переменные типа A - SomeProduct, как в A<SomeProduct> = new C();.

Вы не можете иметь только переменную типа A (которая предоставляет метод addProduct) уже без указания фактического значения аргумента типа T. С другой стороны, вызов метода addProduct не был бы возможен в вашем решении, как описано выше.

Вы мог бы ввести не-сильно типизированные методы сейчас:

public abstract class A<T> 
    where T : ProductBase 
{ 
    protected abstract void addProduct(T p); 

    protected void addProductUntyped(ProductBase p) 
    { 
     T typedProduct = p as ProductBase; 
     if (typedProduct != null) { 
      addProduct(typedProduct); 
     } else { 
      throw new ArgumentException("p has an incompatible type."); 
     } 
    } 
} 

Однако такое решение, как правило, на самом деле требуется только тогда, когда есть некоторые пользователи класса, которые могут знать общий тип время другие - нет.

1

Честно говоря, для реализации кода, я бы просто жить с броском.Единственный раз, когда я хотел бы сделать это «довольно», если это влияние общественных звонящих:

protected override void addProduct(ProductBase p) 
{ 
    SomeProduct prod = (SomeProduct)p; 
    // ... 
} 

Один вариант, что я делать не как много себя, как дженерики:

public abstract class A<T> where T : ProductBase 
{ 
    protected abstract void addProduct(T p); 
} 

public class C : A<SomeProduct> 
{ 
    protected override void addProduct(SomeProduct p) 
    { 
     // ... 
    } 
} 

В Причина, по которой мне не нравится, заключается в том, что вы не можете просто использовать неоригинальный A, так что вы теряете много абстракции. Другим вариантом является new метод повторного объявления параметров. К сожалению, вы не можете new и override на том же уровне, но обходной путь заключается в использовании 2 метода:

public abstract class A 
{ 
    protected void addProduct(ProductBase p) { addProductImpl(p);} 
    protected abstract void addProductImpl(ProductBase p); 
} 

public class C : A 
{ 
    new protected void addProduct(SomeProduct p) { addProductImpl(p);} 
    protected override void addProductImpl(ProductBase p) 
    { 
     SomeProduct prod = (SomeProduct) p; 
     // ... 
    } 
} 
+1

Спасибо за ответ и объяснения, я ценю это. Я иду с маршрутом дженериков. Я понимаю, что вы говорите, но я предпочитаю безопасность типа, что экземпляр C может передавать только SomeProduct для addProduct, а не другой тип, который также наследует ProductBase. Но еще раз спасибо. знак равно – bgs264

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