1. Advanced C # Extracted from (with very minor modifications) Mark Sapossnek CS 594 Computer Science Department Metropolitan College Boston University Find the original set on WWW.GotDotNet.com Jim Fawcett CSE 681 – Software Modeling & Analysis
2.
3.
4.
5.
6.
7.
8. Interfaces Example public interface IDelete { void Delete(); } public class TextBox : IDelete { public void Delete() { ... } } public class Car : IDelete { public void Delete() { ... } } TextBox tb = new TextBox(); IDelete iDel = tb; iDel.Delete(); Car c = new Car(); iDel = c; iDel.Delete();
9.
10.
11.
12. Classes and Structs Differences No user-defined parameterless constructor Can have user-defined parameterless constructor No destructor Can have a destructor No inheritance (inherits only from System.ValueType ) Can inherit from any non-sealed reference type Value type Reference type Struct Class
13.
14. Classes and Structs Class public class Car : Vehicle { public enum Make { GM, Honda, BMW } Make make; string vid; Point location; Car(Make m, string vid; Point loc) { this.make = m; this.vid = vid; this.location = loc; } public void Drive() { Console.WriteLine(“vroom”); } } Car c = new Car(Car.Make.BMW, “ JF3559QT98”, new Point(3,7)); c.Drive();
15. Classes and Structs Struct public struct Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public int X { get { return x; } set { x = value; } } public int Y { get { return y; } set { y = value; } } } Point p = new Point(2,5); p.X += 100; int px = p.X; // px = 102
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36. Classes and Structs Virtual Methods class Shape { public virtual void Draw() { ... } } class Box : Shape { public override void Draw() { ... } } class Sphere : Shape { public override void Draw() { ... } } void HandleShape(Shape s) { s.Draw(); ... } HandleShape(new Box()); HandleShape(new Sphere()); HandleShape(new Shape());
37.
38. Classes and Structs Abstract Methods abstract class Shape { public abstract void Draw(); } class Box : Shape { public override void Draw() { ... } } class Sphere : Shape { public override void Draw() { ... } } void HandleShape(Shape s) { s.Draw(); ... } HandleShape(new Box()); HandleShape(new Sphere()); HandleShape(new Shape()); // Error!
39.
40. Classes and Structs Method Versioning class Derived: Base { // version 1 public virtual void Foo() { Console.WriteLine("Derived.Foo"); } } class Base { // version 1 } class Base { // version 2 public virtual void Foo() { Console.WriteLine("Base.Foo"); } } class Derived: Base { // version 2a new public virtual void Foo() { Console.WriteLine("Derived.Foo"); } } class Derived: Base { // version 2b public override void Foo() { base.Foo(); Console.WriteLine("Derived.Foo"); } }
41.
42.
43.
44.
45.
46.
47.
48.
49. Classes and Structs Operator Overloading struct Vector { int x, y; public Vector(x, y) { this.x = x; this.y = y; } public static Vector operator +(Vector a, Vector b) { return Vector(a.x + b.x, a.y + b.y); } ... }
50.
51.
52. Classes and Structs Implementing Interfaces public interface IDelete { void Delete(); } public class TextBox : IDelete { public void Delete() { ... } } public class Car : IDelete { public void Delete() { ... } } TextBox tb = new TextBox(); IDelete iDel = tb; iDel.Delete(); Car c = new Car(); iDel = c; iDel.Delete();
53.
54.
55.
56.
57.
58.
59. Delegates Overview delegate double Del(double x); // Declare static void DemoDelegates() { Del delInst = new Del(Math.Sin); // Instantiate double x = delInst(1.0); // Invoke }
60.
61. Delegates Multicast Delegates delegate void SomeEvent(int x, int y); static void Foo1(int x, int y) { Console.WriteLine("Foo1"); } static void Foo2(int x, int y) { Console.WriteLine("Foo2"); } public static void Main() { SomeEvent func = new SomeEvent(Foo1); func += new SomeEvent(Foo2); func(1,2); // Foo1 and Foo2 are called func -= new SomeEvent(Foo1); func(2,3); // Only Foo2 is called }
73. Attributes Querying Attributes [HelpUrl("http://SomeUrl/MyClass")] class Class1 {} [HelpUrl("http://SomeUrl/MyClass"), HelpUrl("http://SomeUrl/MyClass”, Tag=“ctor”)] class Class2 {} Type type = typeof(MyClass); foreach (object attr in type.GetCustomAttributes() ) { if ( attr is HelpUrlAttribute ) { HelpUrlAttribute ha = (HelpUrlAttribute) attr; myBrowser.Navigate( ha.Url ); } }
74.
75. Preprocessor Directives Overview Delimit outline regions #region , #end Specify line number #line Issue errors and warnings #error , #warning Conditionally skip sections of code #if, #elif , #else , #endif Define and undefine conditional symbols #define , #undef Description Directive
76. Preprocessor Directives Conditional Compilation #define Debug public class Debug { [Conditional("Debug")] public static void Assert(bool cond, String s) { if (!cond) { throw new AssertionException(s); } } void DoSomething() { ... // If Debug is not defined, the next line is // not even called Assert((x == y), “X should equal Y”); ... } }
77.
78.
79. XML Comments Overview Formatting hints <list>, <item>, ... Use of a parameter <paramref> Cross references <see>, <seealso> Sample code <example>, <c>, <code> Property <value> Exceptions thrown from method <exception> Permission requirements <permission> Method return value <returns> Method parameter <param> Type or member <summary>, <remarks> Description XML Tag
80. XML Comments Overview class XmlElement { /// <summary> /// Returns the attribute with the given name and /// namespace</summary> /// <param name="name"> /// The name of the attribute</param> /// <param name="ns"> /// The namespace of the attribute, or null if /// the attribute has no namespace</param> /// <return> /// The attribute value, or null if the attribute /// does not exist</return> /// <seealso cref="GetAttr(string)"/> /// public string GetAttr(string name, string ns) { ... } }
81.
82.
83. Unsafe Code Overview class FileStream: Stream { int handle; public unsafe int Read(byte[] buffer, int index, int count) { int n = 0; fixed (byte* p = buffer) { ReadFile(handle, p + index, count, &n, null); } return n; } [dllimport("kernel32", SetLastError=true)] static extern unsafe bool ReadFile(int hFile, void* lpBuffer, int nBytesToRead, int* nBytesRead, Overlapped* lpOverlapped); }
84.
85.
Editor's Notes
This module is designed to update C++ and Java programmers with the fundamentals of C#.
The term object sometimes refers to an instance and sometimes to a class. It’s meaning can usually be determined by context.
An interface is a well-defined set of methods (functions). Interfaces do not contain data members. Type is different from class. The type specifies the interfaces, the class specifies the implementation.
Polymorphism is achieved through: Inheritance: a base type can have multiple derived types Interfaces: multiple types can implement a given interface Late binding: you can use any object, as long as it implements the methods you want to call. In C# you can use reflection to dynamically determine if an object implements a method, and then call it. Interfaces can also be used in a late-bound manner. In order to handle multiple types without polymorphism you have to write conditional code (using if or switch statements) that tests the type of an instance and then runs the appropriate code. Such code is brittle and not easily extended. Many Object-Oriented design concepts are motivated by minimizing dependencies. You want to be able to develop independent modules, so that making a change to one doesn’t force you to have to go back and change others.
In scenarios where completely different objects need to support some kind of shared functionality like, let’s say, persist to XML, classes can implement interfaces that make them compatible with even if they don’t share the same base class. This provides most of the benefits of multiple class inheritance without the nasty side-effects that this usually brings. Interface members are implicitly public and abstract.
If there’s no ambiguity then you do not have to specify the name of the interface.
Classes and structs provide a way to create user-defined types.
The C++ and C# structs are alike in name only.
protected internal = protected OR internal. No way to define protected AND internal.
Constants are compiled into client programs, while readonly fields are not.
Having to declare ref in the caller helps make the code more readable and maintainable.
In the example, the Add function can be called with a variable number of arguments. Here, there are 3 arguments. Each is put into the intArr array. The function then iterates over the array, producing the sum of the 3 numbers.
When you call a non-virtual method M (DoSomething) on a class C (Foo), you are guaranteed that C.M (Foo.DoSomething) will be called.
Let’s say that we have a base class that doesn’t do anything interesting. BUILD: Then a user of this class inherited this class and create a method named Foo() BUILD: Then the creator of the original base class realizes how Foo-ness is important and decided to implement that in the base class. Then we have a problem. How should this behave? Should the clients keep calling Derived.Foo or Base.Foo. In C# it is guaranteed that Derived.Foo will be the one being called because it will most likely preserve the behaviour expected by the client. However the compiler will issue an warning saying that method Base.Foo was hidden. BUILD: To suppress the warning, the programmer can use the keyword new, and explicitly hide the method Base.Foo. BUILD: Or if he can just override as any virtual method.
If a class defines any constructors, then the implicit parameterless constructor is not created.
B.B() is needed because once B.B(int h) was defined, the default parameterless constructor is no longer created. Can’t call both : base() and : this() because it would then be ambiguous as to how the base class is constructed.
Java inner classes implicitly have access to the instance of their containing class that created them. C# nested types have no such implicit relationship. However, such a relationship can easily be implemented by passing a reference to the containing class to the nested class, which then saves that in a field.
Delegates are essentially function pointers. This is the mechanism used to implement event in the frameworks. In the example we first declare a delegate for a function that returns double and receives a double as a parameter. We then instantiate the delegate, telling it to use Math.Sin as the function. Finally we invoke the delegate we created. The power is in the fact that once instantiated, a delegate can be passed around and then invoked at some later time.
Since attribute values are stored with the generated code and can be examined by the code in run-time through a mechanism called reflection, you can infinitely add new attributes that will extend the functionality of your C# code. So just as an example, as a CORBA vendor, instead of having to create CORBA wrappers and utilities to write marshalling code, you can define a set of attributes that will instruct the CORBA broker how to marshal the data, also this could be applied to do add information on how to do object to relational database mapping or XML persistence.
Compiler directives are supported just like in C++ with a notable exception of Macros. The reason is that macros can change the behavior of the code just by changing a define. It also makes debugger really harder and also makes a lot more difficult to make proper optimizations. However, there is a attribute called Conditional that can be used to suppress methods based on a define. This is how a type-safe Assert is implemented in C# without macros.
If you need to do some real low level memory manipulation, you can declare a method unsafe and then you have access to full C pointer syntax. You can think of unsafe methods as inline C.
Just declare the unsafe keyword and you can do full memory manipulation