Sie sind auf Seite 1von 26

C#.

NET
Method Overloading and Overriding:When inheriting from another class, you may wish to change the default behavior of a method or create a different method signature. You can do this by overloading the method with your own Code. Method Signature are formed from the parameter list, in particular the data types. Methods can share the same name within the class as long as the signature is different.

static int Add(int a, int b) { return a + b; }

In the above example the signature should be (int,int). In the VS Code editor, if you were to call Add Method, after you enter the ( IntelliSense will popup the available parameters. Notice that there is only one option given to you. We can add an overloaded method with different signature and IntelliSense will now present two options, one for each signature of the overloaded method. The compiler finds two methods of the same name and two calls to that method with different parameters. It is able to tell the difference by comparing the signature of the methods.

static int Add(int a, int b, int c) { return a + b + c; }

The names of the parameter and the return type have no effect on the method signature.

static decimal Add(int a, int b, int c) { return a + b + c; }

The above method will raise an error because a method already exists for Add(int, int), even though it returns a Decimal.

Methods should be overloaded when you have similar methods that require different parameters or you want to add new functionality to existing code, however you should not use overloading too often as it causes headaches during debugging and testing and is more effort to maintain.

Optional Parameters in C#
C# does not have an implementation of optional parameters like those found in php, however you can simulate this feature using method overloading. static int Add(int a, int b) { return Add(a, b, 0, 0); } static int Add(int a, int b, int c) { return Add(a, b, c, 0); } static int Add(int a, int b, int c, int d) { return (a + b + c + d); } In this example we can call Add with 2, 3 or 4 parameters and only one functional method is called. The others just pass the data around.

Override
Methods can be overridden, replacing the default behaviour or extending it. In this example we will override the ToString method for the ArrayList class. using System; using System.Collections.Generic; class Program { static void Main() { ArrayList myList = new ArrayList();

myList.Add("Hello"); myList.Add("World"); myList.Add("123456"); Console.WriteLine(myList.ToString()); } } You may expect the ToString to return "Hello World 123456 , but it actually returns "System.Collections.ArrayList", which isn't particularly helpful. We can override this default behaviour by creating a new class, which inherits from the ArrayList. using System; using System.Collections.Generic; class Program { static void Main() { myArrayList myList = new myArrayList(); myList.Add("Hello"); myList.Add("World"); myList.Add("123456"); Console.WriteLine(myList.ToString()); } } class myArrayList : System.Collections.ArrayList { public override string ToString() { string result = ""; string[] theItems = (string[])base.ToArray(typeof(string));

foreach (string item in theItems) { result += " " + item; } return result; } } The base keyword is used to refer to the object. By default the override method will return base.ToString(). You can use this base object to call the non-overwritten method. Now when we create an instance of our new class and call the ToString method we are given "Hello World 123456 . In Visual Studio 2005 onwards you can get the IDE to create the code for the override for you. Create the class and public override then select the method from the list. It will then fill in the remainder of the code and default return for you.

Delegates and Events


Delegates allow your code to dynamically change the method that it calls. This allows a mthod to change, based on the code that invokes it. Events are deligate methods that are invoked on a certain condition.

Delegates
A delegate type is a reference type, which allows an indirect call to a method. It holds a pointer to a method declared elsewhere, and when the delegate is invoked, it in turn invokes the method it points to. Delegate methods are very similar to C++ function pointers, except that a delegate type can point to multiple methods. In this example we call a OutputText method with a string and a output method and the delegate type, Write, sorts out whether to write to a file or write to the screen.

using System using System.IO

public class Program {

// Define our delegate type public delegate void Write(string theText);

// Method for output to screen public static void OutputToScreen(string theText)

{ Console.WriteLine(theText); }

// Method to output to file public static void WriteToFile(string theText) { StreamWriter fileWriter = File.CreateText("c:/delegate-demo.txt"); fileWriter.WriteLine(theText); fileWriter.Flush(); fileWriter.Close(); }

public static void Main() { // Assign a method to a delegate Write toFile = new Write(WriteToFile); Write toScreen = new Write(OutputToScreen);

Display("This is a delegate demo", toFile); Display("This is a delegate demo", toScreen); }

public static void Display(string theText, Write outputMethod) { outputMethod(theText); } } A bit of a long example, but it is good to illustrate how the delegate can be used to determine the method to call for outputting the text, either to file or to screen. We start off by defining the delegate in the same way, as we would start to declare a method. We use the delegate keyword to mark the method, and because there are no implementations we terminate the statement with a semicolon. Next we define two methods, which could have the delegate pointing to. They must have the same method signature as the delegate defined above.

In the main method we assign an instance of the delegate type to each of our test output methods, then we call a generic Display method with the text to process and the display method. This Display method invokes the output message with the text passed in.

Multicast Delegates
A multicast delegate can call to multiple methods using the += operator.

public static void Main() { // Assign a method to a delegate Write toFile = new Write(WriteToFile); toFile += new Write(OutputToScreen); In this example, when toFile is invoked, both WriteToFile and OutputToScreen will be called. You can remove delegates using the -= operator:

public static void Main() { // Assign a method to a delegate Write toFile = new Write(WriteToFile); toFile += new Write(OutputToScreen); toFile -= WriteToFile;

When invoking toFile, it will only call OutputToScreen now.

Events
Events are similar to delegates, except that a delegate has to be called, whereas an event is a response to a condition. Examples of common events are Button_Click, Page_Load and Mouse_Over. Events can also be multicast, assigned to and removed in the same way as a multicast delegate.

this.Load += new System.EventHandler(this.Form1_Load); The concept of events is based on a subscription-notification model. Any class must be able to "subscribe" to an event and receive a "notification" whenever the events occur. When a class subscribes to an event, it declares its interest in the event, and when it receives the notification it has its delegate methods run. In the following example shows how testClass2 subscribes to an event issued by testClass1.

y y y

testClass1 issues events. It has a multicast delegate, eventD. testClass2 subscribes to the event with its method eventHandler by adding a reference to eventD. When testClass1 issues an event it invokes eventD, which causes eventHandler to fire.

Creating Events
You can create events in your code in a similar way that properties are declared by defining a public event and a protected method.

public class Program { public delegate void EventDelegate(object sender, EventArgs args); public event EventDelegate testEvent;

protected virtual void onTestEvent(EventArgs args) { if (testEvent != null) testEvent(this, args) } } You should always check that the delegate is not null before calling it, as you could end up with exceptions being thrown.

Events and Inheritance


If your class inherits from a class, which already issues or responds to events, you can intercept these actions by overriding the method used to raise it.

protected override void onMyEvent(EventArgs args) { Console.WriteLine("The Event Stops Here"); } This will also prevent any "subscriptions" from receiving their "notifications". If you wish subscribers to continue to receive the notifications then simply call the base event method.

protected override void onMyEvent(EventArgs args) { Console.WriteLine("The Event Passed Here and Carried on"); base.onMyEvent(args); }

Attributes
Attributes provide a means of changing the behavior of a method or class. You can setup conditional flags, mark a method as a web method, obsolete or allow a class to be serialized to XML. Attributes are specified in square brackets before the class or methods that they are to apply to.

[WebMethod] public string getPageTitle { return "Test Code"; }

WebMethod and WebService


Lets first look at web method, as it was used in the above example. In Web Services, all methods are all hidden from direct access to the Internet. You need to specify, using the WebMethod attribute, any method you want to expose to the Internet.

public string HelloWorld() { return "Hello World"; }

[WebMethod] public string web_HelloWorld() { return "Hello World"; } In the above example, anybody connecting to the web service will only be able to invoke the web_HelloWorld method even though the regular HelloWorld is marked as public. You must also mark the class as a web service using the WebService attribute. This attribute accepts parameters in the form of a namespace.

[WebService(Namespace="http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class Service : System.Web.Services.WebService { ... In the above example you can see how to apply more than one attribute to a class.

Serializable
When you mark an object as serializable you are informing the compiler that at some point you want to save the object as XML data stream. You can also implement the ISerializable interface. To serialise an object you can use an XML Serializer and StreamWriter to write to a file.

using System; using System.Xml.Serialization; using System.IO;

[Serializable] public class myTestClass { public string myString = "Hello World"; public int myInt = 1234; public string[] myArray = new string[4];

public string myMethod() { return "Hello World"; } }

class Program { static void Main() { myTestClass test = new myTestClass(); test.myArray[0] = "qwerty"; test.myArray[1] = "asdfgh"; test.myArray[2] = "zxcvbn"; test.myArray[3] = "123456";

XmlSerializer mySerializer = new XmlSerializer(typeof(myTestClass)); StreamWriter myWriter = new StreamWriter("c:/mtTestClass.xml"); mySerializer.Serialize(myWriter, test);

myWriter.Close(); } } When run this will create an XML document containing the current data of the object.

<?xml version="1.0" encoding="utf-8"?> <myTestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <myString>Hello World</myString> <myInt>1234</myInt> <myArray> <string>qwerty</string> <string>asdfgh</string> <string>zxcvbn</string> <string>123456</string> </myArray> </myTestClass>

Deserialize Objects
Deserialization is the opposite of serialization, reading the data from XML and converting it into an object. The process is very similar to the serialization method above.

static void Main() { myTestClass test;

XmlSerializer mySerializer = new XmlSerializer(typeof(myTestClass)); FileStream myFileStream = new FileStream("c:/mtTestClass.xml",FileMode.Open);

test = (myTestClass)mySerializer.Deserialize(myFileStream);

// Just to prove it works Console.WriteLine(test.myArray[0]); }

Conditional
The conditional attribute can be set on methods so that they can be called only if that condition is true. An example of this is a debug log. When the program is compiled for a debug build, the method will run each time it is called, however when compiled for release the method will never be called. The method is still compiled into the assembly regardless of the target build.

class Program { [Conditional("DEBUG")] static void debugMethod(string message) { Console.WriteLine(message); }

static void Main() { Console.WriteLine("This is a test"); debugMethod("This is debug message"); Console.WriteLine("This is a test"); } } When the program is compiled for debug the console window will show:

This is a test

This is a debug message

This is a test

When run for a release build the console window will show:

This is a test

This is a test

Obsolete
The obsolete attribute is used to mark a method that should not be used any more. It allows for backward compatibility when using assemblies.

[Obsolete("This class is obsolete, please use testClass instead.")] class oldTestClass { public string Hello() { return "Hello"; } }

class testClass { public string Hello() { return "Hello World"; } }

class Program { static void Main() { oldTestClass test = new oldTestClass(); test.Hello(); } } This will display a warning message at compile time with the obsolete message. It will not stop any other code from using the method, or break any existing code; it will only show the message to developers.

More Attributes
There are many more attributes, you can find them all by inputting a [ and invoking IntelliSense to see a list.

Multithreaded Applications
Threading allows more than one path of concurrent code execution. It is becoming increasingly useful now multi-core and multi-processor systems are becoming more widespread. The concept of threading goes right the way down to processor level. Single thread code has a single execution path on the processor, each line of code may represent several CPU instructions, and they are all executed sequentially. During a long running operation no other execution path will be run from the same application. Threading and multitasking are not related, multitasking is a feature of the operating system whereby it allocates processor time slices to allow multiple programs to access the CPU. Threading allows a program to execute two or more execution paths in parallel. Threading does not require a multi-processor multi-core processor to run. Multi-threaded applications open up more instruction paths allowing two or more code paths to be run at the same time. This technique allows a long processing operation to run in the background, while the user interface runs in a separate thread. Alternatively you can split a long job into to sections, or run two operations at the same time. The .Net framework manages all the complexity of multi-threaded applications, all you need to do is create a threading object and tell it what to do. It's literally that easy.

using System;

class Program { static void Main() { doSomeWork();

for (int i = 0; i < 10; i++) { Console.WriteLine("Main Method Loop: " + i.ToString());

// Delay so we can visibly see the work being done Thread.Sleep(1000); } }

protected static void doSomeWork() { for (int i = 0; i < 10; i++) { Console.WriteLine("doSomeWork Loop: " + i.ToString());

// A different delay so we can see difference Thread.Sleep(500); } }

} In this example, the main method calls the doSomeWork method which is processed, then it processes the for loop within main. The resulting output is: If we want both loops to run at the same time we need to create a thread to run the doSomeWork method. using System; using System.Threading;

class Program { static void Main() { ThreadStart threadWork = new ThreadStart(doSomeWork) Thread thread = new Thread(threadWork); thread.Start();

for (int i = 0; i < 10; i++) { Console.WriteLine("Main Method Loop: " + i.ToString());

// Delay so we can visibly see the work being done Thread.Sleep(1000); } }

protected static void doSomeWork() { for (int i = 0; i < 10; i++) { Console.WriteLine("doSomeWork Loop: " + i.ToString());

// A different delay so we can see difference Thread.Sleep(500); } }

} Now when you run the program you will notice that the main loop and the doSomeWork loop are now both running at the same time. The Thread.Sleep is there purely so you can see each line being written to the screen. You can remove it and the program will run and complete instantly. The parameter value is the time to delay in Milliseconds (1000ms = 1 sec.)

When to Multi-thread
Multi-threading should not be used because it is seen to be cool, it should only be used when there is a real need for it. Threaded applications soon become a minefield of debugging hell. Threads are unpredictable, and results can be turned to garbage it its not done properly. Imaging two threads running, A and B. A depends on a value, S, to perform a calculation. What happens if thread B changes the value of S while A is still running? Threading should only be considered if the time taken to perform the calculation is series is unacceptably slow, or if two tasks are unconnected and able to run parallel. You should also ensure that all resources are capable of supporting multiple concurrent connections, can the database server accept two update commands at the same time or is it working in table-lock mode? Only when you are comfortable that threading is the only way forward should you go down this route.

Thread Safety
Thread safe code protects the thread from some of the problems outlined above. Thread safety highlights the inherent weakness of a multi-thread system: the need for multiple threads to access the same shared data, but not at the same time. How to tell if code is thread safe? Does the thread access data on the heap or any global variables? Does it access any data through reference or pointers? Is it reliant on a file, database connection or similar to be present?

Recursion Techniques
Recursion is the process where by a method calls itself until an exit condition is met. Very useful, but also very easy to shoot yourself in the foot. Recusion is used where a method processes items, finds more and calls itself to process the items it finds. An example would be a directory tree listing. It first scans the root of the C: drive, then calls itself for each directory found. If a subdirectory also contains directories another call to itself is made. Recursion is also used in mathematics and scientific programming, node programming, artificial intelligence and some database applications. Recursion is a good way of reusing code and processing multiple levels of data, however it is very easy for the system to run away. You also need to pass data to the method that can get memory intensive. It will also have an impact on performance. The following code sample illustrates a recursive method for listing a directory structure.

class Program { static void Main() { StringBuilder dirList = new StringBuilder();

dirList = directoryListing("c:/Inetpub", ""); Console.WriteLine(dirList.ToString()); }

static StringBuilder directoryListing(string path, string indend) { StringBuilder result = new StringBuilder(); DirectoryInfo di = new DirectoryInfo(path); DirectoryInfo[] directories = di.GetDirectories();

foreach (DirectoryInfo dir in directories) { result.AppendLine(indent + dir.Name);

// Call the method again on the directories found result.Append(directoryListing(dir.FullName, indent + "..").ToString()); }

return result; } } This routine will scan through the directory specified in the Main method, grab all the sub directories within that directory, add them to the result, then call itself on each of the subdirectories found. It will recurse until a folder has no subdirectories. Finally it returns the result back to the line that called the recursive method, until there are no more recursions to be processed. The result is then handed back to the Main method call. Each time the recursive method is called you need to tell it where to start from, in this case the folder to look in. In this example I also use a parameter for the indent level. Each recursion adds two dots to the previous indent level so you can see the directory structure clearer. It is very easy for a recursive method to run away with itself, an invalid parameter or start point will cause the method to call itself over and over again and never progress. In this case you will receive a StackOverflowException, which should be handled with a try catch block on the first call of the method.

Regular expressions
Regular expressions are special strings that are used to describe a search pattern. They can be used for data validation, data processing and pattern matching. Regular Expressions (regex for short) are language and platform independent so an expression for Perl will work on PHP and C#. Regex start with a caret (^) character and end with a dollar ($) symbol. The text in between these two characters is used to match a string. A good introduction regular expressions, pattern matching and syntax can be found onZytrax.com Regular expressions in C# are defined within the System.Text.RegularExpressions namespace which provides a Regex class. When instantiating the class you need to pass the expression string to the constructor. We have used a verbatim string for the regex as it makes the regex easier if you don't have to escape forward slashes. In this example we test two strings to see if they contain a valid email address. The emailRegex will match any valid email address.

string ValidEmailAddress = "somebody@somedomain.com"; string InvalidEmailAddress = "invalid.email-address.com&somedomann..3"; string emailRegex = @"^[\w-]+(\.[\w-]+)*@([a-z0-9-]+(\.[a-z0-9-]+)*?\.[az]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?$";

Regex RegularExpression = new Regex(emailRegex);

if (RegularExpression.IsMatch(ValidEmailAddress))

Console.WriteLine("{0}: is Valid Email Address", ValidEmailAddress); else Console.WriteLine("{0}: is NOT a Valid Email Address", ValidEmailAddress);

if (RegularExpression.IsMatch(InvalidEmailAddress)) Console.WriteLine("{0}: is Valid Email Address", InvalidEmailAddress); else Console.WriteLine("{0}: is NOT a Valid Email Address", InvalidEmailAddress); Regular Expressions can be used for input validation, but they can also be used for search and replace functions, extracting values from a string, ASP.Net Input Validation, web.config ISAPI rewrite rules and many other uses.

Unsafe Code Execution


C# and any other .Net language is a managed environment. This means that, among other things, in the background a hidden process called the Garbage Collector cleans up unused memory and old references to make room for new. Managed code is the direct responsibility of the .Net CLR which looks over things like memory management. The problem with managed code is the garbage collector process that removes unused memory locations and references and will cause other pointers to change reference as a result. For this reason pointers are only allowed within the unsafe code block. By marking a section of code as "unsafe" you take away this responsibility from the CLR to the programmer. It is now the coders responsibility to clean up objects and memory after they are used. The advantage of this method is that code is much faster to execute as a lot of the overhead of managed code is eliminated.

Defining Unsafe Code


You are required to mark code as "unsafe" to prevent accidental use of pointers (especially from C++ programmers). Unsafe code can be marked by simply enclosing a section of code in a unsafe block:

...

unsafe { ... // this code is unsafe - pointers can be used. ... } ...

Unsafe can also be used as an access modifier when declaring methods:

unsafe public static void myUnsafeMethod { ... // this code is unsafe - pointers can be used. ... }

Pointer Notation
As well as specifying blocks of code as unsafe, you must also tell the compiler that you want to allow unsafe code blocks to be executed. This is done from within the project options. Under the Project menu select properties and under the Build tab there is an option to "Allow unsafe code blocks". Once this is set and you are within an unsafe code block you can use pointers. This should give you an indication of how dangerous pointers are. Pointers are declared in the same was as in C++.

int normalInteger; int *pointerInteger; Note the use of an asterisk before the variable name. In this example, normalInteger is assigned the value of an integer (default = 0) , whereas pointerInteger is assigned the memory location of an integer. The asterisk can also be placed after the type, e.g. int* pointerInteger.

What is a Pointer?
A normal variable is a container that holds information of a particular type (int, string, enum and so on). A pointer variable does not hold a value or a type, but instead it contains a memory location that points to the container. When you read and write to a pointer variable you are getting and setting the memory location (also known as an address).

In the above diagram we can see 5 memory locations, each with a type and a value. The size and length of the memory location will vary depending on the size of the type used, for example an integer is a 4 byte value type, so each memory location of type int is 4 bytes long. If we declare a standard variable, e.g. int myInt = 52432, then the container identified as myInt contains the value 52432. This value cannot be changed, so we say that it is an immutable type. When you re-assign a value to it later, e.g. myInt = 89343, another int type is created and myInto points to that value. This is true for all value types. When we declare a pointer to an int, e.g. int *myInt, we are assigning the container myInt the memory location of 4 available bytes in memory, in this case 5. In reality there are many hundreds of thousands of memory addresses which are represented in hex. For simplicity we are just using simple numbers. If you were to assign a value to a pointer type, e.g.

int *myInt; myInt = 2; Then you are assigning a different memory location to the variable. In this example myInt now points to memory location 2, which is a string. Because integers are 4 bytes long, when you try and access the value of the pointer, it will only return the first 4 bytes of the string. If you assign to the pointer then the original string will become corrupt. By changing pointers around it is very easy to corrupt or overwrite data in memory and cause the operating system to error or even crash. This is why pointers are dangerous and should only be used when it is essential to use them. There are valid reasons for using pointers, for example, applications which feature large numbers of complex mathematical operations where speed is paramount i.e. a device driver.

Basic usage of Pointers


Pointers are used slightly different to normal variables. If you use a pointer as you would a variable then you will more than likely cause memory corruption.

unsafe

{ int* x; int y = 10; x = &y; Console.WriteLine("Value of x is: " + *x); }

Value of x is: 10

Fixed Keyword
The Garbage Collector will re-arrange memory locations as it cleans up, causing a problem with pointers to objects being moved but the pointer still points to the old location. To overcome this problem there is a fixed keyword that can be used to pin the object and makes sure that the garbage collector will not relocate the object. Color cl = new Color(); unsafe { Console.WriteLine("Red Component of Color is: {0}", cl.R); fixed (byte *pRed = &cl.R) { *pRed = 122; } Console.WriteLine("Red Component of Color is: {0}", cl.R); }

Generics

Generics are a new feature in the .Net framework 2.0+ which make it possible to design classes and methods that do not specify data types until the class or method is declared and instantiated by client code. By using a generic type parameter you can write a single class that other client code can use without incurring the cost or risk of casts or boxing operations. In this first example we can see how a method can accept an array of various types and output the values. Without generics you would need to override the method for each data type, causing problems for scalability and custom types.

using System; using System.Collections.Generic;

class Generics { static void Main(string[] args) { // create arrays of various types int[] intArray = { 1, 2, 3, 4, 5, 6 }; double[] doubleArray = { 1.0, 2.0, 3.0, 4.0, 5.0 }; char[] charArray = { 'A', 'B', 'C', 'D', 'E' };

DisplayArray(intArray); DisplayArray(doubleArray); DisplayArray(charArray);

Console.ReadLine(); }

// generic method displays array of any type static void DisplayArray<E>(E[] array) { Console.WriteLine("Display array of type " + array.GetType() + ":"); foreach (E element in array) Console.Write(element + " "); } } You can see how the one method will handle requests for an array of ints, doubles and chars. We can also use reflection and the GetType method to determine the type of array passed into the generic parameter. In the next example we can see a generic class with a generic method and a generic field. We can create an instance of the class as an int or a string and populate the field and call the method with parameters of the same type as the generic class, but if you create an instance of type int, you cannot assign strings to it.

using System; using System.Collections.Generic;

class Generics

{ static void Main(string[] args) { MyGeneric<int> mygenericint = new MyGeneric<int>(); mygenericint.GenericField = 13; mygenericint.GenericMethod(42);

MyGeneric<string> mygenericstring = new MyGeneric<string>(); mygenericstring.GenericField = "xxx"; mygenericstring.GenericMethod("xxx");

// These lines will cause a compile error MyGeneric<int> mygenericint2 = new MyGeneric<int>(); mygenericint2.GenericField = "xxx"; mygenericint2.GenericMethod("xxx"); } }

public class MyGeneric<T> { public T GenericField; public void GenericMethod(T t) { Console.WriteLine("GenericMethod parameter type: " + t.GetType()); } }

Aggregation and Advanced Scope


This section deals with advanced access modifiers, such as internal, aggregation and namespaces and touches on modules and assemblies. Lets start off by quickly looking at the final access modifier, the internal keyword. This is access modifier makes the code accessible at the component or assembly level. Internal classes, methods and properties are similar to the friendship concept in C++ and Visual Basic.

Internal access allows the code to be accessed from any other code compiled within the same assembly.

// Test Assembly: testAssembly.cs // compile with /target:library

public class testClass { internal static int myInt = 9; }

public static int Test() { return myInt; } Test Application:

// Test Program // Compile with /reference:testAssembly.dll

class Program { static void Main() { testClass myTest = new testClass();

// Will not compile as test application 1 is not a member // of the test assembly. myTest.myInt = 10;

// OK to use a getter method. int demo = myTest.Test(); } } Internal methods cannot be accessed from outside the assembly they are compiled to, but you can use a getter and setter to modify the data. Internal access prevents a private field from being overwritten to allow direct access to the data.

Aggregation
Small objects are not really too useful on their own, but when lots of small objects come together and form a much larger object, then you start to see the power of object orientated programming. Aggregation is the name given to this grouping of objects. Think of a Windows form. The form by itself isn't really much use to anybody, nor is a button, input box or drop down list, but when these objects are combined together they form an object that is useful. The power of object orientation is in the relationships between the objects, not in the individual objects themselves. Objects are built from other objects to form object collaborations, or aggregates. This is where the internal access modifier is most used. Public objects have unlimited access, and you may wish to secure this data, but private access does not allow the objects to interact. Protected cannot be used as the objects are not inheriting from other objects. The internal modifier allows a private-like access level to all objects within the collaboration. You can also have a protected internal access modifier, which, as you may think, allows access to the class from the current assembly or from types derived from the containing class. Aggregation is not the same a inheritance. Aggregation and inheritance both provide ways to create larger classes from smaller classes, but they do this in completely different ways. Aggregation is a relationship between objects, whereas inheritance is a relationship between classes. There is no special coding syntax for aggregation; it is just the way that objects are constructed. In this example, the combination of the wheel, engine and brakes objects form the aggregation of the car object.

class Wheel { int Diameter; }

class Engine { int Cylinders; }

class Brakes { int force; }

class Car { theWheels[] = new Wheel[4]; theEngine = new Engine(); theBrakes = new Brakes(); }

Das könnte Ihnen auch gefallen