|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionI expected that learning C# would be easy considering my extensive C++ background. Interestingly, I haven’t found this to be particularly true. There are subtle differences between the languages which will trip up the C++ programmer, and there’s quite a few new things in C# (and features that C++ has but are missing from C#) to make life just a little more complicated than I expected. I realized that while I am “functional” in C#, I am far from being “proficient”. I figured it was time to get back to basics and really explore the language in the way languages used to be taught in the old days: start with the keywords, then move into the syntax, and then start writing some basic (as in simple!) programs. So, for all those interested, this article gives a synopsis of the various keywords in C#. The reader should be familiar with C++ or at least the concepts of classes and objects. In some ways, this is a beginner’s tutorial, but in other ways, it can be a very useful quick reference guide to the C++ veteran (especially the use of the A really good place to begin is this site provided by Microsoft. The KeywordsFollowing is a table of the keywords in C#. Unfortunately, they are organized alphabetically, instead of categorically.
Note that “ Let’s take this list and create some categories so that we can better organize and understand the keywords. Now, a disclaimer: I am not a compiler expert, nor have I cracked open a book on programming in a long time, so you’ll have to forgive me if you don’t like the categorization of some of your favorite keywords! Keywords that modify a class, method, property, or field:
Keywords dealing with object type and type conversions:
Keywords that are aliases to built in types, are specific types, or have to do with enumerations:
Keywords that control program flow:
Keywords that are used in exception handling:
Keywords that are like C++ function pointers and related subjects:
Keywords that affect garbage collection:
Keywords to help with critical sections of code:
Keywords that declare scope:
Keywords that control object allocation/destruction:
Keywords that affect method parameter passing:
Keywords that are literals or refer to the current instance of an object:
Keywords dealing with unmanaged code:
Keywords, miscellaneous:
Keywords That Modify AccessibilityIn Part I, I'll look specifically at the keywords that modify the accessibility of classes, methods, fields and properties. MSDN provides excellent descriptions of these keywords, so I have two choices here—I can either plagiarize what they’ve already done, or I could write my own descriptions based on my own understanding, which may or may not be as complete, accurate or useful. I opted for the second option, as this way, I’ll end up writing little code snippets to try things out, and I’ll have learned something along the way (whether you will learn something, well, I don’t know about that). abstractAbstract class When used as a modifier for a class, this keyword declares the class as being intended to be used only as a base class for other classes. You cannot directly instantiate an abstract class. For this reason, the Abstract classes are useful when providing generic functionality to a class structure. As the name implies, an abstract class should represent the abstraction of some object. Abstract method When used as a modifier for a method, the method does not have an implementation—the implementation must be provided by the derived class. Because abstract methods are implicitly virtual (meaning a derived class must provide the implementation), the derived class must use the keyword Abstract methods are useful as a placeholder for a common process shared by specific implementations. Unlike virtual methods, in which the base class can provide some generic implementation, an abstract method forces the programmer to provide an implementation in all derived classes. This may be done because there is no generic implementation that is reasonable to provide. Abstract attribute An attribute can be abstract as well, which also requires that its implementation be provided by the derived class. Furthermore, abstract attributes cannot be static because they must be implemented in the derived class. Abstract attributes are useful in the same situations as described for abstract methods. Example: abstract class AbstractClass
{
public int i=5;
public void Method()
{
Console.WriteLine("AbstractClass::Method() i="+i.ToString());
}
// can't have an implementation:
public abstract void AbstractMethod();
// the property cannot have an implementation either:
public abstract int I
{
get;
}
}
class ConcreteClass : AbstractClass
{
// because there's an abstract method in the inherited class,
// we have to provide the implementation here. Note that we must
// also provide the "override" keyword.
public override void AbstractMethod()
{
Console.WriteLine("ConcreteClass:AbstractMethod");
}
// similarly, for the property:
public override int I
{
get {return i;}
}
}
// can't do: AbstractClass abstractClass=new AbstractClass();
ConcreteClass concreteClass=new ConcreteClass();
concreteClass.Method();
concreteClass.AbstractMethod();
Console.WriteLine("I="+concreteClass.I.ToString());
const and readonlyThe In C++, the void Foo() const; or that the method returned a reference that could not be modified: const int& Foo();
or that the parameter being passed as a reference could not be modified by the method: void Foo(const int& i); which could lead to some ridiculous but valid statements: const int& Foo(const int& i) const; Happily, C# treats the keyword Declaring fields as const protects both you and other programmers from accidentally changing the value of the field. Also note that with const fields, the compiler performs some optimization by not declaring any stack space for the field. For example: const int foo=10;
int bar=16;
results in stack space being used for 00000020 mov dword ptr [ebp-8],10h
but not for Console.WriteLine("foo="+foo.ToString()+", bar="+bar.ToString());
00000096 mov dword ptr [ebp-18h],0Ah ; foo
0000009d mov edi,dword ptr ds:[01C40070h]
000000a3 lea ecx,[ebp-18h]
000000a6 call dword ptr ds:[02EA2758h]
000000ac mov dword ptr [ebp-1Ch],eax
000000af mov eax,dword ptr ds:[01C40074h]
000000b5 mov dword ptr [ebp-20h],eax
000000b8 lea ecx,[ebp-8] ; bar
Furthermore, const HasConstField hcf2=new HasConstField();
Example: class HasConstField
{
public HasConstField(); // can't do: {i=3;}
public const int i=1;
public int I
{
get {return i;}
// can't do: set {i=5;}
}
}
HasConstField hcf=new HasConstField();
// can't do: hcf.i=3;
The For example (from MSDN): public class ReadOnlyTest
{
class MyClass
{
public int x;
public readonly int y = 25; // Initialize a readonly field
public readonly int z;
public MyClass()
{
z = 24; // Initialize a readonly instance field
}
public MyClass(int p1, int p2, int p3)
{
x = p1;
y = p2;
z = p3;
}
}
}
externThe using System.Runtime.InteropServices;
and is used in conjunction with the Example: [DllImport("User32.dll")]
public static extern int MessageBox(int h, string m, string c, int type);
MessageBox(0, "Test", "My Message Box", 0);
internalThe For classes, an internal class and its members (public or not) are not visible to any programs that reference the assembly. For methods, fields and properties, using the Also, all Example: // This class is only visible to this assembly.
internal class InternalClass
{
}
class InternalTestClass
{
// this field is visible only within this assembly.
internal int i=2;
// this method is visible only within this assembly.
internal void InternalMethod()
{
Console.WriteLine("InternalMethod");
}
// this protected method can only be invoked by derived classes.
internal protected void InternalProtectedMethod()
{
Console.WriteLine("InternalProtectedMethod");
}
// this attribute is visible only within this assembly.
internal int I
{
get {return i;}
set {i=value;}
}
}
InternalTestClass itc=new InternalTestClass();
itc.I+=5;
Console.WriteLine("I="+itc.i.ToString());
itc.InternalMethod();
new, override, and virtualWell, just when I thought C# tried to give only one meaning to keywords, I discover this gem. The keyword To explicitly hide a member inherited from a base class First off, the Example 1: class NewBaseClass
{
public void NonVirtualMethod()
{
Console.WriteLine("NewBaseClass:NonVirtualMethod");
}
}
class NewClassA : NewBaseClass
{
public new void NonVirtualMethod()
{
Console.WriteLine("NewClassA:NonVirtualMethod");
base.NonVirtualMethod();
}
}
In this example, the Using new and virtual together guarantees a new point of specialization Now, things get more interesting when the inherited method has been declared as virtual. First, take a look at the following C# classes: Example 2A: class VirtualClass
{
public virtual void VirtualMethod()
{
Console.WriteLine("VirtualClass:VirtualMethod");
}
}
class OverrideClassA : VirtualClass
{
public override void VirtualMethod()
{
Console.WriteLine("OverrideClassA:VirtualMethod");
base.VirtualMethod();
}
}
class OverrideClassB : OverrideClassA
{
public override void VirtualMethod()
{
Console.WriteLine("OverrideClassB:VirtualMethod");
base.VirtualMethod();
}
}
and the test code: Example 2B: OverrideClassB ocb=new OverrideClassB();
ocb.VirtualMethod();
((OverrideClassA)ocb).VirtualMethod();
((VirtualClass)ocb).VirtualMethod();
resulting in: OverrideClassB:VirtualMethod OverrideClassA:VirtualMethod VirtualClass:VirtualMethod OverrideClassB:VirtualMethod OverrideClassA:VirtualMethod VirtualClass:VirtualMethod OverrideClassB:VirtualMethod OverrideClassA:VirtualMethod VirtualClass:VirtualMethod Now compare the above class definition with the following: Example 2C: class NewBaseClass
{
public virtual void VirtualMethod()
{
Console.WriteLine("NewBaseClass:VirtualMethod");
}
}
class NewClassA : NewBaseClass
{
public new void VirtualMethod()
{
Console.WriteLine("NewClassA:VirtualMethod");
base.VirtualMethod();
}
}
class NewClassB : NewClassA
{
public new void VirtualMethod()
{
Console.WriteLine("NewClassB:VirtualMethod");
base.VirtualMethod();
}
}
And note the use of the Example 2D: NewClassB ncb=new NewClassB(); ncb.VirtualMethod(); ((NewClassA)ncb).VirtualMethod(); ((NewBaseClass)ncb).VirtualMethod(); we get: OverrideClassB:VirtualMethod OverrideClassA:VirtualMethod VirtualClass:VirtualMethod OverrideClassA:VirtualMethod VirtualClass:VirtualMethod VirtualClass:VirtualMethod Now, the cast allows us to access the specific base class member, because the derived class provides a new specialization, meaning that it doesn’t simply provide an implementation of the base class method, it provides a specialization. This is a critical distinction. When implementing a class, the programmer must consider whether the class methods provide specialization vs. always override the base class functionality. One rule of thumb is to always assume specialization, except in the case of abstract classes, methods or interfaces, all of which must be overridden by the derived class. Note that if the inherited class’ method has been declared private, it is already hidden from the derived class and therefore the One final point—inspecting the resulting assembly code shows that there is no difference in performance between invoking members that are modified with the private, protected, and publicThese three modifiers affect the accessibility of methods, fields and attributes. The These three modifiers are fundamental to a good object oriented design. Much progress can be made towards reducing potential bugs by restricting visibility and access to methods, fields and attributes. However, inappropriate use of these modifiers can result in classes that are not extensible because visibility/access is incorrectly configured. For example: class PPP
{
private int a=0;
public int A
{
get {return a;}
set {a=value;}
}
protected int A2
{
get {return a*2;}
set {a=value/2;}
}
public int GetA() {return a;}
protected int GetA2() {return a*2;}
private void SetA2(int a2) {a=a2/2;}
}
sealedThe staticA method, field or property that has been modified with the For example: class StaticClass
{
public static int a=0;
public int A
{
get {return a;}
set {a=value;}
}
}
StaticClass sc1=new StaticClass();
StaticClass sc2=new StaticClass();
sc1.A=10;
Console.WriteLine("sc2.A="+sc2.A.ToString());
StaticClass.a=20;
Console.WriteLine("sc1.A="+sc1.A.ToString());
Console.WriteLine("sc2.A="+sc2.A.ToString());
Outputs: Sc2.A=10
Sc1.A=20
Sc2.A=20
Illustrating how even though volatileThis keyword modifies how the compiler generates code that reads and writes the contents of a field that uses this keyword. Using the This keyword is useful when an external process (like the operating system or a thread in your application) can potentially modify the value of a field, and you need to ensure that you are reading the most current value. Unfortunately, I cannot seem to get the C# compiler to generate code to demonstrate the differences between volatile and non-volatile memory reads, even with the optimization flag turned on. This is probably because of poor optimization in C#. voidThe ConclusionBy thoroughly reviewing the keywords in C#, especially the modifier keywords, the transition from C++ to C# can be much less confusing and much simpler for the programmer.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||