2014-02-20 2 views
2

У меня возник вопрос о генераторах C#. Я хочу сохранить общую переменную типа в моем абстрактном классе, не объявляя этот тип вне класса.Защищенный общий класс - поддерживается ли он?

Ниже приведен образец кода. Обратите внимание, что я не хочу, чтобы классы Param были выставлены снаружи Calc класс.

Заранее спасибо. - Dutta.

abstract class Base { } 

abstract class Calc<T> where T : Base 
{ 
    protected Param Member; /* how can this be a made a generic declaration 
          * WITHOUT declaring this class like, 
          * class Calc<T, P> 
          *  where T : Base 
          *  where P : Param */ 

    protected Calc(Param p) 
    { 
     this.Member = p; 
    } 

    protected abstract class Param { } 
} 

class MyBase : Base { } 

class MyCalc : Calc<MyBase> 
{ 
    public MyCalc() : base(new MyParam()) { } 

    public void doSomething() 
    { 
     base.Member.A++; // fails on compilation 
    } 

    private class MyParam : Calc<MyBase>.Param 
    { 
     public int A; 

     public MyParam() { this.A = 0; } 
    } 
} 
+1

Почему вы хотите, защищенный класс? Что вы пытаетесь выполнить/предотвратить? Что не работает? То, что у вас есть, выглядит нормально – Thraka

+0

Мне нужен защищенный класс ** Param **, поскольку я хочу, чтобы только классы, полученные из ** Calc **, могли создавать экземпляры и вызывать методы на нем. –

+0

Это другое требование, чем сказать, что вы не хотите, чтобы параметр типа «Param» был «общедоступным». –

ответ

1

Вам просто нужно привести его к новому типу, потому что независимо от того, что переменная-член объявлена ​​как Param и он всегда будет доступен в качестве Param:

((MyParam)base.Member).A++; 

Во-вторых, вы можете исправить свой класс MyParam, изменив от этого:

MyParam : Calc<MyBase>.Param 

К этому:

MyParam : Param 

Поскольку Param уже Calc<MyBase> через дженерики и наследование.

+0

Совершенно верно, я написал его таким образом, чтобы код был явным. У меня нет ошибки компиляции в этой строке. –

+0

Я не заметил, что вы указали, где у вас была ошибка. Я обновил ответ, чтобы отразить то, что, как я думаю, вам нужно сделать. – Thraka

+1

Обновленный ответ Thraka правильный. Приведение необходимо, если вы не используете дженерики. Независимо от того, типы, используемые для параметров типового типа, должны иметь ту же видимость, что и сам общий класс, и, в вашем случае, ту же видимость, что и параметр, выставленный из класса. –

0

Ответ Трахки верен: если вы не хотите использовать дженерики, вам нужно бросить. Просто добавьте к нему, если то, что вы действительно пытаетесь сделать, выглядит примерно так. Вот набор классов, которые вы можете открыть из своей библиотеки, которые не будут расширяться клиентами (если только они не работают с полным доверием и не могут использовать отражение и т. Д. !!), но которые можно использовать безопасным способом.

public abstract class SupportedPaymentMethod 
{ 
    protected internal SupportedPaymentMethod() { } 
} 

public sealed class Check : SupportedPaymentMethod 
{ 
    public int CheckNumber { get; private set; } 

    public Check(int checkNumber) 
    : base() 
    { 
     CheckNumber = checkNumber; 
    } 
} 

public sealed class CreditCard : SupportedPaymentMethod 
{ 
    public CreditCard() 
    : base() 
    { } 
} 

public abstract class Payment<T> 
    where T : SupportedPaymentMethod 
{ 
    public T Method { get; private set; } 

    protected internal Payment(T method) 
    { 
     Method = method; 
    } 
} 

public sealed CheckPayment : Payment<Check> 
{ 
    public CheckPayment(Check check) 
    : base(check) 
    { } 
} 

public sealed CreditCardPayment : Payment<CreditCard> 
{ 
    public CreditCardPayment(CreditCard creditCard) 
    : base(creditCard) 
    { } 
} 

Клиенты (т.е. код вне сборки вашего класса библиотеки) будет иметь возможность создать экземпляр CheckPayment или CreditCardPayment, но они не смогут создать новый класс, производный от Payment<T>. Таким образом, клиенты не смогут создать, например, CheatingPaymentMethod : Payment<Cheating>. :)

вызовов как предполагаемый вызов к base.Member.A++ теперь будут работать:

var checkPayment = new CheckPayment(new Check(123456)); 
var checkNumber = checkPayment.Method.CheckNumber; // Success! :)