Inheriting the Stack class

ddanbe 1 Tallied Votes 776 Views Share

Inheritance with generic types can sometimes be tricky.
If you would inherit from a Stack<double>, no problem, but what if you want to keep it as generic as possible?
Maybe it's a bad idea, but I wrote something like class Stacker<T> : Stack<T> and in Stacker I implemented some arithmetic methods like divide now if T is a double, all is well, but if T is a Person class? To divide two Persons would be hard to do. So our smart C# compiler does not let you use arithmetic operators here. This means T + T or T / T etc. are not allowed. Luckily the designers of C# thought of that too and introduced the dynamic keyword. Which essentialy overrides the compiler checking in this case.
So before using arithmetic methods, it has become the resposability of the programmer to do the the checking here.
The extra methods I wrote are just some help in making life easier in working with a stack. I left most of the error checking out, to make it a bit clearer. Enjoy!

using System.Collections.Generic;

namespace StackTest
{
    class Stacker<T> : Stack<T>
    {
        public Stacker()
        { }

        /// <summary>
        /// Duplicate top of stack: n -- nn
        /// </summary>
        public void DUP()
        {
            T v = this.Pop(); this.Push(v); this.Push(v);
        }

        /// <summary>
        /// Multiply(if possible) the two top items on the stack
        /// replace with the result
        /// </summary>
        public void MUL()
        {
            if (Count >= 2)
            {
                dynamic v1 = Pop();
                dynamic v2 = Pop();
                Push(v1 * v2);
            }
        }

        /// <summary>
        /// Sum(if possible) the two top items on the stack
        /// replace with the result
        /// </summary>
        public void ADD()
        {
            if (Count >= 2)
            {
                dynamic v1 = Pop();
                dynamic v2 = Pop();
                Push(v1 + v2);
            }
        }

        /// <summary>
        /// Substract(if possible) the two top items on the stack
        /// replace with the result
        /// </summary>
        public void SUB()
        {
            if (Count >= 2)
            {
                dynamic v1 = Pop();
                dynamic v2 = Pop();
                Push(v2 - v1);
            }
        }

        /// <summary>
        /// Divide(if possible) the two top items on the stack
        /// replace with the result
        /// </summary>
        public void DIV()
        {
            if (Count >= 2)
            {
                dynamic v1 = Pop();
                dynamic v2 = Pop();
                Push(v1 / v2);
            }
        }

        /// <summary>
        /// Negate(if possible) the top item on the stack
        /// replace with the result
        /// </summary>
        public void NEG()
        {
            dynamic v1 = Pop();
            Push(-v1);
        }

        /// <summary>
        /// Rotate the 3 th item to the top of the stack: n1 n2 n3 -- n2 n3 n1
        /// </summary>
        public void ROT()
        {
            if (Count >= 3)
            {
                dynamic v3 = Pop();
                dynamic v2 = Pop();
                dynamic v1 = Pop();
                Push(v2);
                Push(v3);
                Push(v1);
            }
        }

        /// <summary>
        /// Remove top item
        /// </summary>
        public void DROP()
        {
            T v = Pop();
        }

        /// <summary>
        /// Swap the two topmost items on the stack:n1 n2 -- n2 n1
        /// </summary>
        public void SWAP()
        {
            if (Count >= 2)
            {
                T v1 = Pop();
                T v2 = Pop();
                Push(v1);
                Push(v2);
            }
        }

        /// <summary>
        /// Copy the second item and make it the topmost:n1 n2 -- n1 n2 n1
        /// </summary>
        public void OVER()
        {
            if (Count >= 2)
            {
                T v1 = Pop();
                T v2 = Pop();
                Push(v1);
                Push(v2);
                Push(v1);
            }
        }
    }
}

// A a sample of possible use:

using System;

namespace StackTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Stacker<double> SD = new Stacker<double>();
            SD.Push(3.0);
            SD.DUP();
            SD.MUL();
            Console.WriteLine("The square of 3 is {0}.", SD.Peek());
            Console.WriteLine("Evaluation of 5*(4-2)");
            SD.Push(2.0);
            SD.Push(5.0);
            SD.Push(4.0);
            SD.ROT();
            SD.SUB();
            SD.MUL();
            Console.WriteLine("The result is {0}.", SD.Peek());
            Console.ReadKey();
        }
    }
}
pritaeas 2,194 ¯\_(ツ)_/¯ Moderator Featured Poster

Nice example. Any particular reason you use T in the first and last three methods, yet dynamic in the others?

ddanbe 2,724 Professional Procrastinator Featured Poster

I only use dynamic in the arithmetic methods(needed btw. or C# won't allow it). It doesn't matter in the others.
If you SWAP two Cars, Persons or doubles, it will all work as such.

JOSheaIV 119 C# Addict

Interesting, didn't know that's what dynamic did. Have you thought of making the classes virtual? So someone could inherit this design and override maybe those problemsome methods?

So if you had two Person objects you tried to add, what would C# actually do in the code during run time? Does it blow some sort of error?

(My trick with this was ListQueue<T> I called it. It was a hidden List variable that offered what I called doubled ended queue abilities meaning you could push/pop/peek from either end.Then I had real fun allowed you to do this but at any index, and included a random version of each).

ddanbe 2,724 Professional Procrastinator Featured Poster

This example is just a scetch of what it perhaps can become.
You can compile the following code, but if you run it, you get a RuntimeBinderExeption.
Perhaps it is better to make dedicated double or int stacks.

class Program
    {
        class Person
        {
            public string Name;
            public int ID;
        }

        static void Main(string[] args)
        {
            Person P1 = new Person { Name = "Tom", ID = 123 };
            Person P2 = new Person { Name = "Mary", ID = 124 };
            Stacker<Person> SP = new Stacker<Person>();
            SP.Push(P1);
            SP.Push(P2);
            SP.ADD();
        }
    }
JOSheaIV 119 C# Addict

Possibly. Another thought would be to find someway to restrict those classes so the call to the function itself requires you to specify a type or something. Like a fail safe the coder must follow. However, even then, not entirely sure how you would do that at the moment, and it does seem like a bad approach from a code perspective.

Well one other theory is you make say a virtual or interface type class that all the custom classes that use the Stack must be built on. That way you can guarantee a column that is of numerical value to add with. However, that can quickly cripple the flexibilty of your code

ddanbe 2,724 Professional Procrastinator Featured Poster

Yes JOShealIV you gave me a hint on how to possibly solve this.
I forgot there was something like type constraints.
So you could do things like class Stacker<T> : Stack<T> where T : double, int, DateTime
When you now try to make a Stacker<Person> class, you will get errors.
I'll investigate this a bit further.
Thanks for the hint. :)

JOSheaIV 119 C# Addict

Oh let me know what you find I haven't looked into type constraints really at all, so knowing they exist now has me interested with what you come up with (hahaha, I had a feeling C# had to have some way to limit with types, just not sure how exactly)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.