C# help

Started by
9 comments, last by superpig 17 years, 1 month ago
I have recently began trying to learn some C# (Unfortunately here in Malta, they still teach Pascal in schools and Colleges so this is my first experience with OOP too.) While experimenting I wrote the following program:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class physgc
        {
            private decimal _xpos;
            private decimal _ypos;
            private decimal _yspeed;
            private decimal _xspeed;
            private decimal _bounciness;
            public delegate void rectasks();
            public static rectasks updateall = null;
            private const decimal _gravity = 0.2M;
            /// <summary>Determines Bounciness of object. 1 is most bouncy while 0 is least bouncy.</summary>
            public decimal bounciness
            {
                get
                {
                    return _bounciness;
                }
                set
                {
                    _bounciness = value;
                    if (_bounciness > 1) _bounciness = 1;
                    else if (_bounciness < 0) _bounciness = 0;
                }
            }
        public decimal xpos //x co-ordinate of object
            {
                get
                {

                    if (_xpos > Console.WindowWidth - 2) _xpos = Console.WindowWidth - 2;
                    else if (_xpos < 0)
                    {
                        _xpos = 0;
                    }
                    return _xpos;
                }
                set { _xpos = value; }

            }
            public decimal ypos //y co-ordinate of object
            {
                get
                {
                    if (Math.Round(_ypos) > Console.WindowHeight - 2) _ypos = Console.WindowHeight - 2;
                    else if (_ypos < 0)
                    {
                        _ypos = 0;
                    }
                    return _ypos;
                }
                set { _ypos = value; }
            }
            public physgc() //Constr.
            {
                Random ran = new Random();
                updateall += update;
                updateall += draw;
                xpos = ran.Next(0, Console.WindowWidth);
                bounciness = (Convert.ToDecimal(ran.Next(0, 95)) / 100);
                ypos = ran.Next(0, Console.WindowHeight);
                _xspeed = ran.Next(0, 15);
                _yspeed = 0;
            }
            public void draw() //draw on screen
            {

                Console.SetCursorPosition(Convert.ToInt32(xpos), Convert.ToInt32(ypos));
                Console.WriteLine("0");
            }
            private void bouncey() //bounces in direction y
            {
                _yspeed = ((_yspeed) * -1) * bounciness;
            }
            private void gravity()
            {
                if (Math.Round(_ypos) < Console.WindowHeight - 2)
                {
                    _yspeed = _yspeed + _gravity;
                }
                else bouncey();
            }
            private void bouncex() //bounces in direction x
            {
                _xspeed = _xspeed * -1;
            }
            private void xmove()
            {
                _xspeed = _xspeed * 97 / 100;
                if ((xpos >= Console.WindowWidth - 2) | (xpos <= 0)) bouncex();
            }
            public void update()
            {
                gravity();
                xmove();
                xpos = xpos + _xspeed;
                ypos = ypos + _yspeed;
            }
        }
    class Program
    {
   
        static void Main(string[] args)
        {
            Boolean quit = false;
            Console.CursorVisible = false;
            do
            {
                physgc ball = new physgc();
                char inp= ' ';
                do
                {                   
                    physgc.updateall();
                    System.Threading.Thread.Sleep(40);
                    Console.Clear();                    
                    if (Console.KeyAvailable)
                    {
                        inp=Console.ReadKey(true).KeyChar;
                        if (inp == 'q') quit = true;
                    }
                }
                while (((Console.KeyAvailable == false) & (inp!='a')) &(quit==false));   //Main Loop             
            } 
            while (quit==false);
            
        }
    }
}

 

With this code, what basically happens is that a ball (drawn with "0") is put randomly on the screen and very simple physics are calculated on it, with the ball reacting to the window edges. A new ball is created when user presses 'a' The code works perfectly but I have a few questions: 1) Is the 'delegate' method I used to keep track of all new balls created so that the update and draw methods can be called on each pass the correct way to do it? Is there another way to call a method on all different instances of a class? 2) If I were to decide to 'delete' a ball what should I do to remove it from memory? 3) (Which might answer 2) Is the implementation I used correct? Since I am new to OOP the code might be absolutely laughable. I do not see a way to reference (as an example), the second ball created, so that I could change its bounciness property. As I said I am completely new to OOP so the above code might be the worst implementation you have ever seen. That's why I came here - to see whether I am going in the right direction. Thanks ;-)
Advertisement
Quote:Original post by Horcruxmt
1) Is the 'delegate' method I used to keep track of all new balls created so that the update and draw methods can be called on each pass the correct way to do it? Is there another way to call a method on all different instances of a class?
2) If I were to decide to 'delete' a ball what should I do to remove it from memory?
3) (Which might answer 2) Is the implementation I used correct? Since I am new to OOP the code might be absolutely laughable. I do not see a way to reference (as an example), the second ball created, so that I could change its bounciness property.


1. It's a perfectly good way. The other common method would be to store a list (System.Collections.Generic.List - it's not a linked list, confusingly, so it's still O(1)) of the objects and then loop for each item in the list. Otherwise, a solution for far larger scale projects (i.e. link provided more for interest than suggestion) is the Model-View-Controller design pattern.

2. Nothing. C# is managed/garbage collected. I'm not sure as to the specific method of garbage collection used (reference counting?), but it'll take care of freeing memory for you. There are slight caveats here with more complex projects - if an object holds unmanaged memory or objects (e.g. handles for kernel mode objects) then you may need to give the garbage collector a nudge in the right direction (by manually altering the GC's tracking of memory usage for specific objects or by judicious use of the Dispose with IDisposable objects).

3. It looks alright to me, at a glance, but I'll let some more experienced designer pick up on that. However, here I think it is worth pointing you towards the link to the Model-View-Controller Wikipedia article above as something to bear in mind for the future.

Few miscellaneous things reading through your code:

while (quit==false) can be switched to while (!quit). It saves you a few characters to type, and is closer to what you say when reading the code (i.e. if quit were named readyToQuit then you'd be able to read it "Not readyToQuit" as opposed to "readyToQuit has the value of false").

In reference to:
while (((Console.KeyAvailable == false) & (inp!='a')) &(quit==false));
You're using the bitwise AND here (i.e. 1 AND 1 = 1, 1 AND 0 = 0) as opposed to logical AND (TRUE AND TRUE = TRUE, TRUE AND FALSE = FALSE) - switch & to &&. Again, the previous note about (quit==false) holds.

Strictly, you should probably provide your namespace (and because it's a default one, your projects and solutions too) with a useful/meaningful name.

EDIT: Trimmed quotation to just the questions
[TheUnbeliever]
Thanks for the reply, however I still have a major question which I can't figure out- How do you call a method for a particular instance if I created another instance with the same name (ball each time user presses a). This is why I thought there was something wrong with my implementation, though I guess (and hope) that there is some easy concept which I have not yet understood to do this.
Quote:Original post by Horcruxmt
Thanks for the reply, however I still have a major question which I can't figure out- How do you call a method for a particular instance if I created another instance with the same name (ball each time user presses a). This is why I thought there was something wrong with my implementation, though I guess (and hope) that there is some easy concept which I have not yet understood to do this.


I'm not sure if I'm understanding you correctly, but you won't have this conflict because you can't create two variables with identical names in the same scope. If you want to create a new ball every time your user presses 'a' (and still keep track of all your previous balls), you'd probably be adding them to some sort of data structure.
Quote:Original post by Horcruxmt
Thanks for the reply, however I still have a major question which I can't figure out- How do you call a method for a particular instance if I created another instance with the same name (ball each time user presses a). This is why I thought there was something wrong with my implementation, though I guess (and hope) that there is some easy concept which I have not yet understood to do this.


I think the concept that you haven't fully understood (I'm sure you have an understanding of it, to an extent, hence the lack of a full and detailed explanation) is scope. Which, thankfully, is pretty easy in C#.

do{    /* The next line creates a new reference (because a class is a reference       type in C#), which we call 'ball', and we point it to the object created       by 'new physgc()' */    physgc ball = new physgc();    char inp= ' ';    do    {                           physgc.updateall();        System.Threading.Thread.Sleep(40);        Console.Clear();                            if (Console.KeyAvailable)        {            inp=Console.ReadKey(true).KeyChar;            if (inp == 'q') quit = true;        }    }    while (((Console.KeyAvailable == false) & (inp!='a')) &(quit==false));                  /* Here, at the end of the curly braces, is the end of the current scope.       This is the scope that the reference named 'ball' was created in, and       so that reference falls out of scope, meaning we can't use it. Because       nothing else holds a reference to that reference, it becomes fodder for       the garbage collector (to free it from memory).       However, the object that 'ball' pointed to was created by 'new' and       therefore does not (itself) have 'scope'. The garbage collector keeps       track of whether or not it's being referenced, and if not, will get       rid of it when it feels it's appropriate (the details of when exactly        this is depend on the garbage collector, and I don't know the       particulars).       Now, our reference ('ball') to this object has just disappeared - so       why doesn't the object get removed? Because our delegate will still call       a member function of that object - and so this object is still being       'used', meaning the garbage collector leaves it well enough alone.       In clarification - you're actually creating a whole bunch of nameless       objects, and repeatedly recreating a reference called 'ball' to point       to the most recent one of these objects. */} while (quit==false);


Another method of performing an operation on each of your objects would be to forego having the delegate, and keep a list. You then use this list in some way to call the member function directly on each element in the list.

Example:

Using System;Using System.Collections.Generic;Using System.Text;namespace ListExample{    class Person    {        String name;        public String Name        {            get { return name; }            set { Name = value; }        }                public Person(String Name)        {            this.Name = Name; // 'this' pointer required to access the member        }        public void Speak()        {             Console.WriteLine("Hey there! My name is " + Name + ".");        }    }    class Program    {        [STAThread]        static public void Main(string[] args)        {            /* First, create a new List of 'Person's (of course, 'People' is our             * name for the reference to that List). The constructor argument             * isn't necessary - but we want to allocate memory for at least             * 100 instances of 'Person' first. */            List<Person> People = new List<Person>(100);            /* Now add 100 instances to that list - note that we don't even need             * to create a reference to our objects here - the list holds the             * only reference we need! */            for (int i = 0; i < 100; ++i)                People.Add(new Person());           /* Now, we want to make each 'Person' tell us their name - so we can            * use a for each loop to call the Speak member function for each            * element of the list */           foreach (Person p in People)               p.Speak();           /* But, how about if we want to do something for each element in the            * list - but not use a member function? We could use a for each            * loop, or we could use the List's ForEach method (and I'm also            * using something called an anonymous delegate/function here, to            * avoid creating a named function for something that might be only            * used once and isn't necessarily reusable. */           People.ForEach(delegate(Person p)                          {                              Console.WriteLine("This guy's name is " + p.Name + ".");                          });        }    }}
[TheUnbeliever]
Ok, I understood what is happening there. So, I guess a solution would be to create an array before entering the loop and then adding each different added ball to a new position. But since arrays in C# are always fixed-size wouldn't that mean that only a certain number of balls can be created? Or is there a more suitable data structure other than an array?
Quote:Original post by Horcruxmt
Ok, I understood what is happening there. So, I guess a solution would be to create an array before entering the loop and then adding each different added ball to a new position. But since arrays in C# are always fixed-size wouldn't that mean that only a certain number of balls can be created? Or is there a more suitable data structure other than an array?


There is - refer back to my example above - use the System.Collections.Generic.List class.
[TheUnbeliever]
Thanks - I noted the List example immediately after posting the previous posts and was experimenting with them when you posted that. In fact I came back here to edit my previous post ;-)
Why don't you use an ArrayList? ArrayLists are dynamic, although limited to one dimension.

I don't know if they(ArrayLists) have any advantages over Lists, or vice-versa for that matter, because I don't know anything about Lists.

But ArrayLists have many of the same functions as Arrays, so I imagine they would work here.
Quote:Original post by theStormWeaver
Why don't you use an ArrayList? ArrayLists are dynamic, although limited to one dimension.

I don't know if they(ArrayLists) have any advantages over Lists, or vice-versa for that matter, because I don't know anything about Lists.

But ArrayLists have many of the same functions as Arrays, so I imagine they would work here.


ArrayLists are from back before C# had generics. I think Lists are pretty much ArrayLists w/ generics. See "Benefits of Generics"

This topic is closed to new replies.

Advertisement