【问题标题】:Protected generic class - is it supported?受保护的泛型类 - 是否支持?
【发布时间】:2014-03-21 21:00:56
【问题描述】:

我有一个关于 C# 泛型的问题。我希望在我的抽象类中存储一个泛型类型变量,而不在类之外声明该类型。

以下是代码示例。请注意,我不希望将 Param 类暴露在 Calc 类之外。

提前致谢。 - 杜塔。

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; }
    }
}

【问题讨论】:

  • 你为什么想要一个受保护的类?你想完成/预防什么?什么不工作?你有什么看起来不错
  • 我需要一个受保护的 Param 类,因为我只希望从 Calc 派生的类能够实例化和调用它的方法。
  • 这与说您不希望 Param 泛型类型参数为“public”不同。
  • 是的,我不想公开。
  • 我的回答对你有用吗?如果是这样,请将其标记为这样。如果没有,请告诉我们原因。谢谢!

标签: c# class generics abstract protected


【解决方案1】:

你只需要把它转换成新的类型,因为无论如何,变量 Member 被声明为 Param 并且它总是作为 Param 被访问:

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

其次,您可以通过以下更改来修复您的 MyParam 类:

MyParam : Calc<MyBase>.Param

到这里:

MyParam : Param

因为通过泛型和继承,Param 已经是Calc&lt;MyBase&gt;

【讨论】:

  • 非常正确——我是这样写的,所以要明确代码。我在该行没有编译错误。
  • 我没有注意到您指出了错误所在。我已经更新了答案以反映我认为您需要做的事情。
  • Thraka 的更新答案是正确的。只要您不使用泛型,强制转换是必要的。无论如何,用于泛型类型参数的类型必须与泛型类本身具有相同的可见性 - 在您的情况下,与从类中公开参数的方式相同。
  • 虽然选角是我想尽可能避免的事情,但我不能否认这确实是一个前进的方向。感谢 Thraka 和 Lars 试图回答这个问题。我已将此标记为正确答案。
【解决方案2】:

Thraka 的回答是正确的:如果您不想使用泛型,则需要强制转换。只是补充一下,以防你真正想做的事情看起来像这样。这是您可以从库中公开的一组类,它们不能被客户端扩展(除非它们以完全信任的方式运行并且可以使用反射等!!)但可以以类型安全的方式使用。

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)
    { }
}

客户端(即类库程序集之外的代码)将能够实例化 CheckPaymentCreditCardPayment,但他们将无法创建派生自 Payment&lt;T&gt; 的新类。因此,例如,客户端无法创建CheatingPaymentMethod : Payment&lt;Cheating&gt;。 :)

像您对 base.Member.A++ 的预期调用一样的调用现在可以工作了:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-15
    • 1970-01-01
    • 2016-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    相关资源
    最近更新 更多