Advertisement

C# Workshop - Week 1 (Ch. 1 & 2) - Advanced

Started by July 01, 2007 12:15 AM
337 comments, last by paulecoyote 17 years, 2 months ago
Quote: Original post by shawnre
15. When are forward declarations needed in C#?

It seems this question in directed towards C++ programmers as it would be strange if the spec would mention all the things that are not needed in C#.
If you don't know C++, don't worry about it. The answer is "No".

Quote:
19. What is the encoding of a C# character, and how much memory does a
single character require?

See character encodings. I don't know the answer myself, and I don't care. I like abstraction.

Quote:
25. What is the relationship between operators and operands in an
expression?

This questions seems out-of-context. We'll have to wait for JWalsh for more information.

Quote:
49. What is the difference between an instance constructor and a static
constructor?

See 10.11.
Quote: Original post by TheTroll
Quote: Original post by Darklighter
How can one declare a method to be const (i.e. unable to modify this)?

Not sure what you are looking for, can you explain?


<warning type="C++ talk">

I'm referring to const member functions...

class Foo{    public:        void Bar() const;};

...which prevent the this pointer from being modified within the function (in getters, for example). The get statement in C# doesn't seem to restrict that, though.

</warning>

Quote: Original post by jpetrie
Quote: Originally by Darklighter:
Out of curiosity, when I use Go to Definition on Console.WriteLine, how come it provides me with a class without implementations? Also, what does [From metadata] refer to?
You get a class without implementation because you don't actually have the source code -- the original implementation -- for System.Console anywhere. What you do have, however, is the .NET assembly that contains the IL and metadata for the class. The IDE can use reflection to read this metadata and produce a skeleton representing the classes interface -- that's what you're seeing, and what's what the "from metadata" tag means.

See this page, scroll down to "Go To Definition Shows Reflected Source (C# only)" or look for Figure 45.


Thanks, that clarifies. And that .NET assembly from which the metadata is retrieved is a .dll, right?

Quote: Original post by SamLowry
Yes. You can do that too in C#. The difference between C++ and C# is that C++ let's you have several base classes (multiple inheritance), while C# relies on interfaces, but these do not allow you to specify default behavior.


Oh, I see. Thanks.

[Edited by - Darklighter on July 5, 2007 1:03:35 PM]
Advertisement
Edit: I draw my conclusion at the end, so feel free to skip the rest, and evaluate that, as that is what I am concerned about.

I have a quick question on static constructors. From the spec(10.11, the section suggested by SamLowry above):
Quote: A static constructor is a member that implements the actions required to initialize a class itself when it is first loaded.

Isn't that definition vague enough to define any constructor, static or not? I am confused by the definition of "first loaded". Am I correct in assuming that this next quote takes care of this definition(also from 10.11):
Quote: The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
- An instance of the class is created.
- Any of the static members of the class are referenced.

<conclusion>What I draw from this is that 1)if an instance of an object with a static constructor is created then the constructor works the same as if it wasn't static and 2)the real difference given in the documentation between a static constructor and a non-static constructor is that the static constructor will be called if a (static) method of a class is invoked without having an actual instance of the class.</conclusion> Is this correct? I could be way off, but the details seem lacking...
Quote: Original post by bschneid
Edit: I draw my conclusion at the end, so feel free to skip the rest, and evaluate that, as that is what I am concerned about.

I have a quick question on static constructors. From the spec(10.11, the section suggested by SamLowry above):
Quote: A static constructor is a member that implements the actions required to initialize a class itself when it is first loaded.

Isn't that definition vague enough to define any constructor, static or not? I am confused by the definition of "first loaded". Am I correct in assuming that this next quote takes care of this definition(also from 10.11):
Quote: The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
- An instance of the class is created.
- Any of the static members of the class are referenced.

<conclusion>What I draw from this is that 1)if an instance of an object with a static constructor is created then the constructor works the same as if it wasn't static and 2)the real difference given in the documentation between a static constructor and a non-static constructor is that the static constructor will be called if a (static) method of a class is invoked without having an actual instance of the class.</conclusion> Is this correct? I could be way off, but the details seem lacking...

Here's an example of constructors in action:
public class App{    public static void Main()    {        Console.WriteLine( "A" );        K k1 = new K();        Console.WriteLine( "B" );        K k2 = new K();        Console.WriteLine( "C" );    }        }public class K{    static K()    {        Console.WriteLine( "Inside the static constructor" );    }    public K()    {        Console.WriteLine( "Inside the instance constructor" );    }}

prints
AInside the static constructorInside the instance constructorBInside the instance constructorC


Quote:
if an instance of an object with a static constructor is created then the constructor works the same as if it wasn't static.

The static constructor does not have a this pointer, meaning it can only access other static members. The static constructor will also only be called only once during the entire lifetime of your application (well... once in an appdomain, but let's not go there).

Maybe an analogy would make it clearer. A class can be seen as a factory which creates objects. The instance constructor is what creates the objects. The static constructor is what creates the factory.

Everything needs to be created only once. Since there is only one factory, the static constructor will only be called once. An instance constructor will be called multiple times, once for each object created.

The creation of the factory is done as late as possible, because hey, it could be that you never need it, and it would be a waste of resources to built one which'll never work (this is called [emph]laziness[/emph] in computer science terminology). So, only when you actually use the factory will it be built. Using it consists of either have it create objects, or communicate with it using static members.
Thanks for the help, that cleared up my questions.

Edit: Thank you also, others who posted below at the same time I did. Its nice to know people are on top of the questions here!
Quote: Original post by bschneid

<conclusion>What I draw from this is that 1)if an instance of an object with a static constructor is created then the constructor works the same as if it wasn't static and 2)the real difference given in the documentation between a static constructor and a non-static constructor is that the static constructor will be called if a (static) method of a class is invoked without having an actual instance of the class.</conclusion> Is this correct? I could be way off, but the details seem lacking...


You are pretty close. The other difference when having a static constructor is when an instance is created of the class is that the static constructor is called and then the instance constructor is called.

theTroll
Advertisement
Quote: Originally by Darklighter:
I'm referring to const member functions which prevent the this pointer from being modified within the method (in getters, for example). The get statement in C# doesn't seem to restrict that, though.

The concept of a const "this" reference or a const object does not exist in C# (although the keyword does, it does something different). There are similar concepts -- readonly and C#'s version of const -- but they do not operate exactly the same way.

Quote: Originally by bschneid:
Isn't that definition vague enough to define any constructor, static or not? I am confused by the definition of "first loaded". Am I correct in assuming that this next quote takes care of this definition(also from 10.11): ...
A static constructor is invoked at some time after the class has been loaded (from the containing assembly) and before the class is used. Static constructors are typically used to initialize static members that have initializations that are too complex to perform inline. A regular (instance) constructor initializes an instance of the class. Keep in mind the distinction between the class itself and instances of the class.

It is possible for code to be structured such that creating a new instance of a class calls both the class's static constructor and the appropriate instance constructor; a static constructor, however, will always be executed before any instance constructors (and for what its worth, inline initialization of static fields in the class happens before the static constructor).
Week 1 Overview

Before I begin, let me tell you a bit about why I decided to use the C# Specification for the C# Workshop, some of the observed problems we're having so far, and how I plan to solve those problems.

First, I decided to use the C# Specification as the official documentation for the C# Workshop because it was both free, complete, and designed for intermediate to advanced programmers. You see, during the C++ Workshop we used a very rudimentary text designed for the complete beginner, and we also moved very slowly. The end result was that people got bored of the workshop, and eventually decided to quit. I wanted to avoid repeating a similar mistake, so instead I created a brand new one. In an attempt to accelerate the workshop to make it more interesting, and to make sure we were all able to use a freely available text, I chose the C# Specification. Unfortunately, I did so without considering how the participants of the C# workshop might be different from those of the C++ Workshop.

First, there's been a much larger interest in the C# Workshop than the C++ Workshop. This has guaranteed it's exposure to a larger audience of people. As well, unlike with C++ where many people were already experienced programmers, there's probably only a dozen or so very experienced C# programmers here on GDNet actively participating in the workshop (out of roughly 400 participants). And finally, C#, unlike C++ is advertised as a very user-friendly and easy to learn language. This has caused far more beginning programmers to join the C# workshop than joined the C++ workshop. What's the end result? The end result is that the C# specification isn't suitable as a beginning learning device, and consequently, not the best suited text for this workshop's audience.

Ok, so we're now working with a text which, although is a good, complete resource - may not be the best resource for the workshop. How do we fix it? Simple. With supplementary text. In specific, I will speak to those people who've volunteered as tutors and either myself or one of them will write an overview for each weekly thread which contains a more tutorial style introduction to the material. The overviews will not go into nearly as much depth as that covered in the specification, but it will provide more examples and it will present some of the most important information in a more human-readable format. I will be writing a partial overview below to give people an idea of what these overviews might be like. Please provide your feedback. If there's not enough interest or usefulness in writing such an overview for each weekly thread, please say so, as it's a time-consuming endeavor on top of an already time-consuming endeavor, and we dont want to burden either myself or the other tutors needlessly if the information and style presented here isn't useful to those beginning C#.

The text assigned to week 1 is chapters 1 and 2. The first chapter serves as an overview of the entire language, giving you just enough information to make you familiar with the terms and concepts of the C# language. Unfortunately, they dont give a slew of examples, nor do they frequently explain the how or the why of something. I dont want to make chapter 1 responsible for scaring people off, suggesting they need to learn the entire language within the first week, however people have already begun reading and are now becoming overwhelmed. At this point, the cat is out of the bag. The only way to move forward as I can see it, is to explain each of those topics a bit further, so they at least make enough sense that people can move forward without feeling like they were left in a deserted field and cluster-f&*!#d. But again, keep in mind that we will go into each topic in more details, one at a time, in the coming weeks. For now, you should just be familiar with the terminology and basic syntax presented here. Ok, let's get it started...

Hello World

It's customary when demonstrating a new language to use the "Hello World" example. Since I'm a fan of tradition, I'll do the same. I'll present the code first, and then explain it in more detail afterwards.
1: class HelloWorldProgram2: {3:     static void Main()4:     {5:         System.Console.WriteLine("Hello World");6:     }7: }  

The above is a very simple example of a completely functional C# application. When compiled it create an executable assembly which, when executed, outputs "Hello World" (without quotes) to the console window. You should enter the above, without the line numbers, into your C# compiler, build and execute the above to see it in action.

Line 1 of the source code is what is called a class declaration. In C# all data and operations exist within a class, and there must be at least 1 class in any C# application. "But what is a class?" For now, just know that a class is a container for all data and operations within an application or library. We'll get into more detail about what a class is and how it's used in a few moments.

Lines 2 and 7 indicate the extents of the class declaration. Everything between the open '{' and close '}' brackets are considered part of the class. All functions, fields, events, delegates, etc...contained between the { and } are called Members of the class.

In this case, we've created a class called "HelloWorldProgram", and we've given it a single method (aka member function) called "Main". On line 3 you can see the declaration for the method called main. You'll also notice that there are two other words in that line - static and void.

Static is a keyword, which in this context, is used to indicate that the method Main is part of HelloWorldProgram, as a class, and does not require an instance of HelloWorldProgram as an Object. The difference between classes and objects will be explained later. For now, just recognize that all C# console programs must have a method called Main, and it must be declared static.

The second thing you'll see on that line is the type name "void". Void is actually a type used to indicate no type at all. Normally, a type name preceding a method name indicates what kind of value is returned from a method. When void is specified, it means the method doesn't return a value. In C#, the return value for main can either be 'int' or 'void'. If int is specified the programmer is responsible for returning a value. If void is specified, the programmer CANNOT return a value, and the value 0 is returned automagically.

On line 5 you see the real meat of the application. Up until this point, everything we've seen so far will exist in every C# console application you ever make. In fact, you could take out line 5, and just save everything else as a template application if you wanted to. (Note, you dont have to, Visual C# 2005 EE does that for you. Whenever you create a new console application, the class and Main are provided for you). On line 5 we have the line:
        System.Console.WriteLine("Hello World");

The above code may at first appear confusing, but when we break it down into sections, it makes much more sense. The first part is "System". Whenever a programmer writes instructions for a computer, they do so using variables which they create. These variables all have types, and every type has a name called its...wait for it...type name. To make sure the name of their types do not collide with other peoples types, they are grouped into namespaces. Think of it this way...lets say my name is Adam. At my university there may have been hundreds of Adams. How would someone know who I am by name from someone else with the same name? Simple, by also using my last name. If you were to say "Adam Smith", then people would immediately know you didn't mean "Adam Johnson". In other words, the last name of the individual helps to qualify WHICH individual is being referred to. Namespaces are the exact same thing. By grouping types into namespaces, we are in essence giving them a last name. And whenever there is confusion about which type to use with a similar name, a programmer can fully qualify it by identifying the namespace.

In the example above, we are wanting to reference the 'Console' class. But in order to do so I must instruct the compiler WHICH console I want to use. In this case, I have fully qualified which Console I want to use by also including its namespace, ie. System. There are alternative ways to indicate which namespace a type belongs to, but for the sake of this introduction I'll leave it at that.

The next part of the instruction above is "WriteLine". An identifier followed by ( followed by some optional information followed by ) is what's referred to as a method in C#. Using a method in that format invokes that method, causing it to execute code. In this case I'm invoking the WriteLine method of the Console class, in the System namespace. I'm also passing the method the parameter "Hello World". This, in whole, prints whatever I pass into WriteLine out to the console window.

Types & Variables

Now that we've taken a look at a basic C# application, lets make it a little more interesting. Lets start by adding some variables using the simple types that the C# language understands.

1:  class CharacterNameProgram2:  {3:     static void Main()4:     {5:         Character myCharacter = new Character();6:         myCharacter.name = "Jeromy Walsh";7: 8:         System.Console.WriteLine(myCharacter.name);9:     }10: }11: 12: class Character13: {14:     public System.String name;14: }

In the above example I've created a new class type called Character beginning on line 12. Character has one Field (variable) which is called name. Name is of type String. This is how you'd declare a publicly accessible field in C#. On line 5 of the Main method I create a new instance of myCharacter. On line 6 I assign the value "Jeromy Walsh" to my character's name. On line 8 I print the name of my character out to the console.

The above is just something to demonstrate how someone might use one of the simple data types. Among the other simple data types are int, byte, char, float, bool, and decimal. There are also enums and structs, but we'll talk more about that later. Lets expand the above to include some additional fields, which describe my Character class.

1:  class CharacterInfoProgram2:  {3:     static void Main()4:     {5:         Character myCharacter = new Character();6:         myCharacter.name = "Jeromy Walsh";7:         myCharacter.age = 27;	8:         myCharacter.weight = 165;9:         myCharacter.middleInitial = 'L';10:		   11:        System.Console.WriteLine(myCharacter.name);12:     }13: }14: 15: class Character16: {17:     public System.String name;18:	public byte age;19:	public int weight;20:	public char middleInitial;21: }

Each of the above fields use a different simple type, and each one accepts different types of values with different ranges. There is a complete table in your text which shows what types of data can go in each type, and what the possible ranges of values are.

Expressions

The next section of this overview deals with expressions. As indicated in your text, an expression can really be anything that returns a value, including allocation of new objects, comparisons, or even method invocations. But the thing people think of most when talking about expressions are mathematical expressions. That is, statements in the form 'x = m * a + b'. So I've created a quick example showing a practical set of expressions.

1:  class CharacterFightProgram2:  {3:      static void Main()4:      {5:          Character rand = new Character();6:          rand.Name = "Rand";7:          rand.Level = 5;8:          rand.HitPoints = 50;9:          rand.Strength = 5;10:         rand.Armor = 5;11:  12:         Character Semirhage = new Character();13:         Semirhage.Name = "Semirhage";14:         Semirhage.Level = 4;15:         Semirhage.HitPoints = 40;16:         Semirhage.Strength = 4;17:         Semirhage.Armor = 4;18:  19:         rand.Attack(Semirhage);20:         System.Console.WriteLine("{0}'s hitpoint are now {1}.", Semirhage.Name, Semirhage.HitPoints);21:  	}22:  }23:  24:  class Character25:  {26:      public void Attack(Character enemy)27:      {28:          int damage = 0;29:          damage = Level + Strength;30:  31:          int resistance = 0;32:          resistance = enemy.Level + enemy.Armor;33:  34:          int totalDamage = damage - resistance;35:         36:          if (totalDamage <= 0) 37:            totalDamage = 1;38:  39:          enemy.HitPoints -= totalDamage;40:      }41:  42:      public int HitPoints43:      {44:          get { return m_Hitpoints; }45:          set46:          {47:              if (value < 0)  m_Hitpoints = 0;48:              else            m_Hitpoints = value;49           }50:      }51:  52:      public System.String Name;    53:      public int Level;54:      public int Strength;55:      public int Armor;56:  57:      private int m_Hitpoints;58:  }

In the program above, you can see several expressions. First, lines 6-10 and again on lines 13-17 we are creating expression statements which assign values to the public fields of type "Character". On line 19 there is method invocation expression which returns void. And finally,
the majority of expressions are happening between lines 28 and 39. Let me explain these lines in a bit more details.

Lines 28, 31, and 34 are not expressions, they are technically local variable declarations, as we're allocating memory and declaring variables via an identifier which will be used throughout the rest of the method.

Line 29 is an expression which computes this character (rand's) damage output, based on a formula of his strength plus his level.

Line 32 is an expression which computes the enemies resistance, based on his level and his armor.

Line 34 is an expression where we compute the total damage done, by subtracting the resistance from the damage possible.

Line 36 is actually an if-statement, however the if-statement contains a boolean expression "totalDamage <= 0" which returns either true or false depending on the value of totalDamage. In the example, it returns false.

And finally, line 39 is an expression which uses the -= operator. This operator can be expanded into the form:

enemy.HitPoints = enemy.HitPoints - totalDamage;

You can now see the expression simply subtracts the totalDamage from the enemy's hitpoints, and then assigns it back to her hitpoins.

In C#, and especially in game development, the majority of your statements will either be expressions, declarations, or conditionals and branching such as if, else, switch, for, and while.

If you've found the above a more human-readable format for learning beginning C#, please let me know. Note, this is again just an overview of the information in week 1. If this format receives enough interest, myself or another tutor will write similar overviews for the remaining chapters. Also, please let me know if the above is detailed enough, not detailed enough, etc...What do you want more of, what do you want less of?

This workshop is really about all of you. If you're not getting what you want, or what you expected from the workshop, please speak up so we know what level we need to cater to. If there's enough interest in this format, I'll begin by completing the information below.

Arrays // Coming soon...

Structs // Coming soon...

Enums // Coming soon...

Classes & Objects // Coming soon...

Interfaces // Coming soon...

Delegates // Coming soon...

Attributes // Coming soon...
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
Chronicles of Elyria (An In-development MMORPG)
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints
Question regarding the base 'object' and boxing/unboxing. Since every type inherits from the base 'object' type, would it be something similar to this?
public class object{    virtual bool Equals(object o);    virtual int GetHashCode();    virtual Type GetType();    virtual string ToString();}public class int : object{    bool Equals(object o);   // These are the int classes implementations    bool Equals(int o);      // This is an overridden implementation    int GetHashCode();    Type GetType();    string ToString();    virtual int CompareTo(int i);    // These are our new functions    virtual int CompareTo(object o); // This is a new overridden function    virtual TypeCode GetTypeCode();}


After typing this, it seems like object may even be using an interface, like so:
interface IObject{    bool Equals(object o);    int GetHashCode();    Type GetType();    string ToString();}public class object : IObject{    bool Equals(object o);        // Because we inherit the IObject interface    int GetHashCode();            // we have to implement these functions    Type GetType();    string ToString();}public class int : object : IObject  // Do we have to specify IObject, or will{                                    // that get inherited because object    bool Equals(object o);           // already inherited it for us?    bool Equals(int i);     // Should this be an interface also, so any class                            // that inherits from int will also have to                            // implement this function?    (rest of the IObject stuff)}


Based on this, if you inherit a class from a class that implements an interface, does the derived class also have to implement the interface or only if they explicitly state they will implement the interface?

If I understand this correctly, if I 'box' my int to an object {object o = (object)int} then I can only use the members of object, unless I cast (which unboxes or reboxes?)
int i = new int();object o = (object)i;o.GetHashCode();       // This calls object.GetHashCode, not int.GetHashCode?o.Equals(someInt);     // This wouldn't work as object doesn't have an Equals                       // function that takes an int, only that takes an object(int)o.Equals(someInt); // Would this be the way to cast (and would that                        // result in a box/unbox?o.Equals((object)someInt); // This would box someInt into an object, and call                           // the object.Equals(object o), correct?
Quote: Original post by hpjchobbes
Question regarding the base 'object' and boxing/unboxing. Since every type inherits from the base 'object' type, would it be something similar to this?

Not exactly. Those types inherit from ValueType, which inherits from object. However, they are treated differently from other typical object types in that they are not reference types. A value type is always copied except when passed using ref or out.
Quote:
After typing this, it seems like object may even be using an interface, like so:
*** Source Snippet Removed ***

No, object is a concrete class that every other class must inherit from (either directly or indirectly). However, certain types (ValueType, string, a few others) are special in that they implement object, but behave differently.
Quote: Based on this, if you inherit a class from a class that implements an interface, does the derived class also have to implement the interface or only if they explicitly state they will implement the interface?
Only if you explicitly state you will implement the interface, or if the parent is an abstract class or interface that inherits from (but does not implement) the interface. That is:
public abstract class ScreamingList : IList //Note: IList inherts from ICollection and IEnumerable, so you have to implement those interfaces too{    public abstract void Scream();    // Other stuff}public class MyScreamingList : ScreamingList{    public override void Scream() { ... }    // Implement IList and associated interfaces as well}

Quote:
If I understand this correctly, if I 'box' my int to an object {object o = (object)int} then I can only use the members of object, unless I cast (which unboxes or reboxes?)

Yes, when boxed you cannot use any methods except those exposed by the object class. When you unbox you can once again access the original types methods.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

This topic is closed to new replies.

Advertisement