Sei sulla pagina 1di 45

C# Language Design

Peter Hallam
Software Design Engineer
C# Compiler
Microsoft Corporation

Overview

Introduction to C#
Design Problems
Future Directions
Questions

Hello World
using System;
class Hello
{
static void Main()
{
Console.WriteLine("Hello, world!");
}
}

C# Program Structure

Namespaces

Type declarations

Classes, structs, interfaces, enums,


and delegates

Members

Contain types and other namespaces

Constants, fields, methods, operators,


constructors, destructors
Properties, indexers, events

Organization

No header files, code written in-line

Program Structure Example


namespace System.Collections
{
using System;
public class Stack: Collection
{
Entry top;
public void Push(object data) {
top = new Entry(top, data);
}

public object Pop() {


if (top == null) throw new InvalidOperationException();
object result = top.data;
top = top.next;
return result;
}
}

Predefined Types

C# predefined types

The root
Logical
Signed
Unsigned
Floating-point
Textual

object
bool
sbyte, short, int, long
byte, ushort, uint, ulong
float, double, decimal
char, string

Textual types use Unicode (16-bit characters)

C# Classes

Single inheritance
Can implement multiple interfaces
Members

Constants, fields, methods, operators,


constructors, destructors
Properties, indexers, events
Nested types
Static and instance members

Member access

public, protected, internal, private

Interfaces

Can contain method declarations; no


code or data
Defines a contract that a class must
support
Classes have one base class, but can
implement many interfaces
interface IFormattable {
string Format(string format);
}
class DateTime: IFormattable {
public string Format(string format) {}
}

Statements and
Expressions

Very similar to C++, with some changes to increase


robustness

No -> or ::; all qualification uses .


Local variables must be initialized before use
if, while, do require bool condition
goto cant jump into blocks
switch statement no fall through
Expression statements must do something useful
(assignment or call)
void Foo() {
i == 1;
}

// error

void Foo() {
if (i = 1) // error
...

C# Design Goals

Simple, Extensible Type System

1st Class Component Support

Robust and Versionable

Preserve existing investments

Problem:
How to Unify the Type System

A single universal base type (object)

All types ultimately inherit from object


Object variable can hold any value
Any piece of data can be stored,
transported, and manipulated with no
extra work

Unification enables:

Calling virtual functions on any value


Collection classes for any type

Unifying the Type System


Desired Picture:
Stream

MemoryStream

object

Hashtable

int

double

FileStream

How to deal with the primitive types


without losing performance?
How to create user-defined types that
are as efficient as int or double?

How to Unify:
A traditional approach (SmallTalk)

Make everything a real object

Performance implications

All objects have a type descriptor or virtual


function table
May require all object to be heap-allocated to
prevent dangle pointers

Behavior and expectation mismatch

int variables can be null

How to Unify:

Dont do it (Eiffel, Java)

Intrinsic types are not classes

Good performance
Cant convert int to Object the
primitive types are in a separate world
Requires special wrapper classes (e.g.,
Integer) to wrap a primitive type so
that it works in the Object world.
Not extensible the set of primitive types
is fixed.

How to Unify:
C# Approach

Types are divides into two kinds:


Reference types and Value types
Reference types are full-featured:

Always allocated in heap


Arbitrary derivation

Value types have restrictions:

Only inherit from object


Cant be used as base classes
Allocated from stack or inline inside other
objects
Assignment copies value, not reference

Unification

Value types dont need type


descriptors or vtables (efficient!)
object does need a type descriptor,
because it can contain any type
Value types become reference types
when they are converted to object

Value is copied to heap, type descriptor


attached
Process is called boxing
When cast back to value type, unboxing
occurs, value is copied out of heap

Boxing and Unboxing

Everything is an object

Any type can can be stored as an object


int i = 123;
object o = i;
int j = (int)o;

123

int

123

123

} Boxing
}

Unboxing

User-Defined Types

C# allows user-defined types to be


either reference or value types
Classes (reference)

Used for most objects

Structs (value)

Objects that are like primitive data (Point,


Complex, etc).

struct Point { int x, y; ... }


Point sp = new Point(10, 20);

C# Type System

Natural User Model


Extensible
Performant

Problem:
Additional Declarative Information

How do you associate information with


types and members?

XML persistence mapping for a type


External code interop information
Remoting information
Transaction context for a method
Visual designer information (how should
property be categorized?)
Security contraints (what permissions are
required to call this method?)

Other Approaches

Add keyword or pragma

Use external file

Requires updating the compiler for each


new piece of information
Information clumsy to find/see
Require duplication of names
Example: IDL files for remote procedures

Use naming patterns

Create a new class or constant that


describes the class/members
Example: Java BeanInfo classes

C# Solution: Attributes

Attach named attributes (with optional


arguments) to language element
Uses simple bracketed syntax
Arguments must be constants of
string, number, enum, type-name

Attributes - Examples
public class OrderProcessor {
[WebMethod]
public void SubmitOrder(PurchaseOrder order) {...}
}
public class PurchaseOrder
[XmlElement("shipTo")]
[XmlElement("billTo")]
[XmlElement("items")]
[XmlAttribute("date")]
}

{
public
public
public
public

Address ShipTo;
Address BillTo;
Item[] Items;
DateTime OrderDate;

public class Button {


[Category(Categories.Layout)]
public int Width { get {} set {} }
[Obsolete(Use DoStuff2 instead)]
public void DoStuff() {}
}

Attributes

Attributes

Attached to types, members, parameters, and


libraries
Present in the compiled metadata
Can by examined by the common language
runtime, by compilers, by the .NET Frameworks, or
by user code (using reflection)

Extensible
Type-safe
Extensively used in .NET Frameworks

XML, Web Services, security, serialization,


component model, transactions, external code
interop

Creating an Attribute

Attributes are simply classes

Derived from System.Attribute


Class functionality = attribute
functionality
Attribute arguments are constructor
arguments

public class ObsoleteAttribute : System.Attribute


{
public ObsoleteAttribute () { }
public ObsoleteAttribute (string descrip) { }
}

Using the Attribute


[Obsolete]
void Foo() {}
[Obsolete(Use Baz instead)]
void Bar(int i) {}

When a compiler sees an attribute it:


1. Finds the constructor, passing in args
2. Checks the types of arguments against
the constructor
3. Saves a reference to the constructor and
values of the arguments in the metadata

Querying Attributes

Use reflection to query attributes

Type type = typeof(MyClass);


foreach(Attribute attr in type.GetCustomAttributes())
{
if ( attr is ObsoleteAttribute ) {
ObsoleteAttribute oa = (ObsoleteAttribute) attr;
Console.WriteLine({0} is obsolete: {1},
type, attr.Description;
}
}

Problem : Versioning

Once a class library is released, can we add


functionality without breaking users of the
class library?
Very important for system level components!

Versioning Problems

Versioning is overlooked in most languages

C++ and Java produce fragile base classes


Users unable to express versioning intent

Adding a virtual method can break a derived


class

If the derived class already has a method of the


same name, breakage can happen

Versioning: C# solution

C# allows intent to be expressed

Methods are not virtual by default


C# keywords virtual, override and new
provide context
Adding a base class member never breaks a
derived class
Adding or removing a private member never
breaks another class

C# can't guarantee versioning

Can enable (e.g., explicit override)


Can encourage (e.g., smart defaults)

Versioning Example
class Base
// version 2
1
{
} public virtual void Foo() {
Console.WriteLine("Base.Foo");
}
}

class Derived: Base


// version 2b
1
2a
{
new public
public
virtual
override
virtual
void
void
void
Foo()
Foo()
Foo()
{{ {
Console.WriteLine("Derived.Foo");
base.Foo();
} Console.WriteLine("Derived.Foo");
} }
}

Interface Implementation

Private interface implementations

Resolve interface member conflicts

interface I {
void foo();
}
interface J {
void foo();
}
class C: I, J {
void I.foo() { /* do one thing */
}
void J.foo() { /* do another thing */ }
}

foreach Statement

Iteration of arrays
public static void Main(string[] args) {
foreach (string s in args) Console.WriteLine(s);
}

Iteration of user-defined collections


foreach (Customer c in customers.OrderBy("name")) {
if (c.Orders.Count != 0) {
...
}
}

Extending foreach
IEnumerable
interface IEnumerable {
IEnumerator GetEnumerator();
}
interface IEnumerator {
bool MoveNext();
object Current { get; }
}

Extending foreach
IEnumerable
foreach (int v in collection) {
// use element v
}
(IEnumerable) ie = (IEnumerable)
collection;
IEnumerator e = ie.GetEnumerator();
while (e.MoveNext()) {
int v = (int) e.Current;

foreach

Problems with IEnumerable

no compile time type checking


boxing when enumerating value types

Solution : A pattern-based approach

The C# compiler looks for:

GetEnumerator() on the collection


bool MoveNext() on the enumerator type
Strongly typed Current on the enumerator
type

foreach - Summary

Some Complexity

Extensible

User collections can plug into foreach

User Model

interface
pattern

Compile-time type checking

Performance

Value type access without boxing

Future Directions

Generics - Prototype

Implemented by MSR Cambridge

Don Syme
Andrew Kennedy

Published Paper at PLDI 2001

Generics - Prototype
class Stack<T>
{
T[ ] data;
void Push(T top) { }
T Pop() { }
}
Stack<string> ss = new Stack<string>;
ss.Push(Hello);
Stack<int> si = new Stack<int>;
ss.Push(4);

Other Approaches C++

Templates are really typed macros


Compile time instantiations only
Require source for new instantiations
Type Parameter Bounds Infered
Good Execution Speed
Bad Code Size

Other Approaches - Java

Type Erasure
No VM modifications
Compile time type checking
No instantiations on primitive types
Execution Speed Casts
Good Code Size
Type Identity Problems

List<String> vs. List<Object>

C# .NET Generics Prototype

prototype .NET runtime is generics


aware
All objects carry exact runtime type
Instantiations on reference and value
types
Type Parameters bounded by base
class and/or interfaces
Runtime performs specialization

C# .NET Generics Prototype

Compile Time Experience

Separate compilation of generic types


Instantiations checked at compile time
Can Instantiate on all types int, string

Runtime Experience

Dynamic Type Specialization


Execution Speed No Extra Casts
Code Size Code Sharing reduces bloat

Potrebbero piacerti anche