C# 2, see http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf - imperative language, Java-like syntax - distinguishes between statements and expressions - preprocessor similar to the one in C/C++, but without parameterized macros (simple #defines are supported) - reference/value types, boxing, unboxing, ref parameters, out parameters - classes, delegates, arrays are reference types; structs and enums are value types - "object" base type of all types, even value types - "decimal" type (28 decimal digits represented without rounding errors) - range exceedance on integral types may be handled by either throwing an exception or overflowing, depending on the current "overflow checking context", which is set by the "checked"/"unchecked" statement and expression int i=9999999; [un]checked { i*=i; } //statement i = [un]checked ( i*i ); //expression - only unchecked exceptions (which is probably a good thing) - resource managament / implicit finalization with using (ClassImplementingIDisposable o = ...) { doSomething();... } //always calls o.Dispose() when exiting // (locally or nonlocally) the block - goto is supported :-) - classes (single inheritance) and interfaces (multiple inheritance), as in Java - "static" (uninstantiatable) classes static class C { ... } - like in Java and C++, classes and methods are not real first-class citizens... - "new" is still a predefined (and non-overridable) operator, not a method of the class object as it should be[tm] - compile-time constants (fields or in block-local scope) defined using "const" - no C++ - like "const" methods - initialization-time constants defined using "readonly" (similar to "final" in Java). Not supported for local variables, parameters or return values :-\ - variable parameter lists: void foo(T1 p1, params T2[] t2s); - implicit and explicit type conversions: e.g. byte->int is implicit, int->byte is explicit (requires a cast). Generally, conversions are implicit iff the target type's range is a superset of the source type's range - can be defined on user-def'd classes/structs using [implicit|explicit] operator OtherType() {....} - properties - allow defining getters and setters for syntactically field-like accesses class C { public SomeType foo { get {...} set {..} } } C c = ...; SomeType aSomeType = c.foo; c.foo = aSomeType; In the setter, "value" is bound to the value to be set. - "operator overloading" similar to C++ - i.e. pre-defined operators (+, -, ==, !=, *, /, <<, >>, [] etc.), no user-definable operators / infix functions, no user-definable precedence rules - index operator (foo[i]) defined as a special property ("indexer"): public SomeType this[i] { get {...} set {..} } - Java's "super" is called "base" - type definitions may be spread over multiple files partial class C {...} ... partial class C {...} - a "delegate" is a method pointer type - e.g. delegate void MyEventType(int arg1, double arg2) - the class of the method the method pointer points to is not part of the delegate - instances point to a method, i.e. they hold object+method (if they point to instance methods) or just method (if they point to class methods) - actually, a delegate is a subclass of System.Delegate - an instance of a delegate is called a "delegate instance" - e.g. (with the "MyEventType" declaration above) public void handler(int arg1, double arg2) {...} //fcn definition //define instance of the delegate, point it to handler MyEventType methPointer = new MyEventType(handler); //call it methPointer(100,33.332); - a delegate instance can also be defined to point to an anynymous, inline-defined function, e.g. MyEventType methPointer = delegate(int i, double d) {....} the block is a closure, i.e. it captures all variables accessible at the definition point (exceptions: ref and out parameters, "this" in struct methods) - QU: shouldn't there really be a generic, instantiatable "delegate" type? e.g. Delegate d = handler; d(100,33.332); - an "event" is an object that's parameterized with a delegate and holds a list of instances of the delegate (i.e. a list of method pointers) which are called when the event is called - e.g. (with the "MyEventType" and "handler" from above) public event MyEventType myevt; //definition of the event ... myevt += new MyEventType(handler); //add delegates to the event myevt += new MyEventType(someobj.handler); myevt(42, 23.55); //call the event (calls all delegates in turn) - explicit interface members... (see ecma-334, §8.9) - type and namespace aliases: - e.g. using MyNS = some.namespace; using MyType = SomeType; using MyType = some.namespace.SomeType; - methods can be explicitly declared to override or hide a method with the same name in a base class - overriding: public override void method(...) {...} - hiding: public new void method(...) {...} this helps the compiler find typos, and also facilitates binary compatibility (in cases where name clashes might arise when new methods are later added to the base class) - "extern aliases" set up separate namespace roots. I.e. there can be more than one namespace hierarchy in the same program. This helps resolving conflicts that would arise if one wants to use several disjunct types from separate assemblies, and all those types have the same fuilly-qualified name e.g. extern alias X; extern alias Y; X::some.ns.Type t1; //or X.some.ns.Type t1; Y::some.ns.Type t2; //dito at link time, the aliases ("X", "Y") are bound to specific assemblies, e.g. csc /r:X=a1.dll /r:Y=a2.dll test.cs - attributes - allow user-defined declarative pieces of information to be attached to types or methods - e.g. //attribute definition [AttributeUsage(AttributeTargets.All)] //(attr. usage during definition) public class HelpAttribute: Attribute { public HelpAttribute(string url) { this.url = url; } public string Topic = null; private string url; public string Url { get { return url; } } } //attribute usage [Help("http://www.mycompany.com/~/Class1.htm")] public class Class1 { [Help("http://www.mycompany.com/~/Class1.htm", Topic = "F")] public void F() {} } - for each attribute usage, the compiler generates a machine-readable association between the attribute object and the class/method it is attached to. This information can later be queried via reflection. Otherwise, attributes are "passive", i.e. it's not possible to directly define any actions that should take place at attribute usage time (as it could be done in more dynamic languages like Lisp/Ruby etc.). Thus, attributes are mainly useful for providing hints to automatic postprocessors or development tools. - generics (types (and methods) parameterized with types) - syntax roughly like templates in C++ - "constraints" may be declared which the parameter types must comply to - e.g. "must implement interface I": class MyType where T: I { .... } compiler only accepts operations on T that can be statically proven to work with all possible instantiations of T (i.e. there is no "static polymorphy" in the C++ sense -- you can't do "T t=...; t.SomeMethod();" unless "SomeMethod" is defined in I (or object)) - this also applies when there is no explicit constraint definition (e.g. just class MyType {...}), i.e. in this case you can only perform operations on instances of T that are allowed on any objects - compromise: bloat vs. performance: the compiler only generates one real implementation of "MyType" for all instantiations of MyType with reference types T (it can easily do this because of the mentioned absence of static polymorphy). However, a separate, optimized implementation of "MyType" *is* generated for each instantiation of MyType with a value type T. - possible kinds of constraints: only "must implement interface/class", specified separately for each type parameter - iterators - special case (probably the most common one) of a coroutine using System.Collections.Generic; public IEnumerable SomeNumbers() { yield return 42; yield return 23; yield break; } foreach (int i in SomeNumbers()) { Console.WriteLine("{0}",i); } //=> outputs 42\n23\n As a special convenience (?), a class can be made "enumerable" by having it implement IEnumerable and and define public IEnumerator GetEnumerator(). I guess providing general coroutines was deemed too scary... - value types are "nullable" - for each value type T there is a "nullable" type T? whose range is that of T plus null - check for whether an instance t of T is currently null: t.HasValue (t==null does the same thing unless operator== was overwritten) - for each defined conversion S->T from a value type S to a value type T, the compiler generates "lifted" conversions S?->T?, S->T? which are "null-propagating" (the result is null if the input was null), and an explicit conversion S?->T which requires a cast and throws a runtime exception if the input was null. Similarly, the compiler generates "lifted", null-propagating versions of non-comparison operators (+, - etc.). Comparison operators (==,!=,>,<,<=,>=) are defined such that null is equal to null and unequal to any non-null value. Also, <,>,<=,>= return false if one or both operands are null (which apparently means that these operations are no longer ordering relations on nullable types -- is that a problem?) - null coalescing operator "??": provides a kind of shortcut-evaluated "or" for nullable and reference types. The expression "a ?? b", where a has a nullable or reference type and A is the type of a and B is the type of B, evaluates to a if a is non-null, and to b otherwise. The type of the expression is A\null (i.e. the "un-nulled" type of a) -- there must be an implicit conversion from B to A\null. This operator may be used to provide a default value in case the left operand is null. E.g. int? a = ...; int x = a ?? 42; //same as int x = a.HasValue? (int)a : 42; ?? is right-associative, i.e. e1 ?? e2 ?? ... ?? en evaluates to the first ex that is not null (or to null if all ex are null)