سؤال

I had a question on C# generics. I wish to store a generic type variable in my abstract class without declaring that type outside the class.

Below is the code sample. Please note that I do not wish to make the Param classes exposed outside the Calc class.

Thanks in advance. - 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; }
    }
}
هل كانت مفيدة؟

المحلول

You just need to cast it to the new type, because no matter what, the variable Member was declared as Param and it will always be accessed as Param:

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

Secondly, you can fix up your MyParam class by changing from this:

MyParam : Calc<MyBase>.Param

To this:

MyParam : Param

Because Param is already Calc<MyBase> through generics and inheritance.

نصائح أخرى

Thraka's answer is correct: if you don't want to use generics you need to cast. Just to add to it, in case what you're really trying to do looks something like this. Here's a set of classes that you can expose from your library, which will not be extensible by clients (unless they're running with full trust and can use reflection etc.!!) but which can be used in a type-safe way.

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

Clients (i.e. code outside of your class library's assembly) will be able to instantiate a CheckPayment or a CreditCardPayment, but they will not be able to create a new class deriving from Payment<T>. So, it will not be possible for clients to create a CheatingPaymentMethod : Payment<Cheating>, for example. :)

Calls like your intended call to base.Member.A++ will now work:

var checkPayment = new CheckPayment(new Check(123456));
var checkNumber = checkPayment.Method.CheckNumber; // Success! :)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top