C# and .NET Secrets - Quiz

13 October 2009 - .NET

We invite you to prove your knowledge about certain subjects concerning Microsoft .NET technology by participating in a monthly quiz. This month the quiz is about C# and .NET secrets. In this article you can reread the questions. Additionally you get background information about the correct answers.

Here is a list of all questions included in the quiz. Use the hyperlinks to jump to the topic you are interested in:

  • Question 1 - A Simple One To Start
  • Question 2 - String vs. System.String
  • Question 3 - Implicitly Typed Variables
  • Question 4 - Value vs. Reference Types
  • Question 5 - Finalizing Objects
  • Question 6 - Lambda Expressions
  • Question 7 - Nullable Types
  • Question 8 - JIT Compiler 

Question 1 - A Simple One To Start

The CTS (Common Type System) does not know about object orientation at all. Programming languages like C# include all the OO logic. They generate procedural IL code behind the scenes. True or false?

That's absolutely not true! The Common Type System supports object oriented and procedural languages. It has support for OO constructs built in.

If you are interested you can download the ECMA C# and Common Language Infrastructure Standards from Microsoft's website. The documents include a good introduction into as well as a detailed documentation of the Common Type System.

Question 2 - String vs. System.String

What is more efficient in respect of memory usage and performance: The use of string or the use of System.String?

The correct answer is It doesn’t matter which one you take, they perform equallystring is a synonym for System.String. Therefore it does not matter if you take string or System.String.

You can prove that the C# compiler and the .NET CLR treats string and System.Stringequally by looking at the generated code in intermediate language. Take a look at the following lines of code. As you can see we define two classes handling strings. One of them (PersonString) uses string to do its job; the other (PersonSystemString) usesSystem.String.

This class uses string.

Mark the use of auto-implemented properties in this sample. This is a new feature of C# 3.0!

internal class PersonString
{
public string FirstName { get; private set; }
public string LastName { get; private set; }

public PersonString(string FirstName, string LastName)
{
this.FirstName = FirstName;
this.LastName = LastName;
}
public void Replace( string TextToFind, string ReplacingText )
{
FirstName = FirstName.Replace( TextToFind, ReplacingText );
LastName = LastName.Replace( TextToFind, ReplacingText );
}
public override string ToString()
{
return FirstName + " " + LastName;
}
}

This class uses System.String.

internal class PersonSystemString
{
public System.String FirstName { get; private set; }
public System.String LastName { get; private set; }

public PersonSystemString(System.String FirstName,
System.String LastName)
{
this.FirstName = FirstName;
this.LastName = LastName;
}
public void Replace(System.String TextToFind,
System.String ReplacingText)
{
FirstName = FirstName.Replace(TextToFind, ReplacingText);
LastName = LastName.Replace(TextToFind, ReplacingText);
}
public override System.String ToString()
{
return FirstName + " " + LastName;
}
}

The implementation of Main uses implicitly typed variables. This is a new featur of C# 3.0! See question 2 for more details about implicitly typed variables.

static class Program
{
static void Main()
{
var pString = new PersonString("Rainer", "Stropek");
System.Console.WriteLine(pString);

var pSystemString = new PersonSystemString("Rainer", "Stropek");
System.Console.WriteLine(pSystemString);
}
}

IL code of the two classes IL code of the two classes

After compiling the program we can use ILDASM (MS Intermediate Language Disassembler) to generate a readable version of the IL:

ildasm.exe /output="$(TargetDir)\$(TargetName).il" $(TargetPath)

If you compare the IL of the two classes you can see that they are (with the exception of their names) absolutely identical.

Question 3 - Implicitly Typed Variables

C# 3.0 introduces implicitly typed local variables. You do not need to specify a type for a local variable any more! The compiler figures out which type to use for you. So what do you think; does the following code work?

namespace ImplicitTypes
{
class Program
{
static void Main()
{
var v = "Hello World!";
v = 10;
System.Console.WriteLine(v);
}
}
}

You can read more about implicitly typed local variables in MSDN.

The correct answer is No, you cannot compile this code.

The code does not compile. The line v = 10; produces the error Cannot implicitly convert type 'int' to 'string'. The reason is that the new keyword var does not meanvariant! It just means that the compiler determines and assigns the most appropriate type during compile time!

Question 4 - Value vs. Reference Types

What is the output of the following program?

namespace ValueVsReferenceTypes
{
class Program
{
static void Main()
{
var stringValue = "Hello World!";
var stringValue2 = stringValue;
stringValue = "Hello Austria!";
System.Console.WriteLine(stringValue2);

var array = new[] { 1, 2, 3, 4 };
var array2 = array;
array[0] = 99;
System.Console.WriteLine(array2[0]);
}
}
}

The correct answer is Hello World! 99.

System.String is called immutable  (read-only) because its value cannot be modified once it has been created.

Although System.String is a class and therefore a reference type you can never change the content of a string. Every modification to a string variable generates a new String object. Therefore the assignment of "Hello Austria!" to stringValue does not affect the value ofstringValue2.

System.Array behaves differently. If you assign an array to another variable only a new reference to the array is generated. A modification of an array element is visible to everyone holding a reference to the array. Knowing this it should be clear to you why array2[0]contains 99 even if this value has been assigned to array[0].

Question 5 - Finalizing Objects

It is a good practise to use destructors in C# just like in C++. Put cleanup code in your class' destructor and the CLR will care for the rest. Is that correct?

The correct answer is Cleaning up in the destructor is fine. But that is not enough!

class MyClass { 
~MyClass() { ... }
}

is the same as

class MyClass { 
protected override
void Finalize()
{ ...
base.Finalize();
}
}

C# knows destructors. A destructor is the same as a Finalize-Method in which you callbase.Finalize(); at the end. However, using finalizers costs performance. Additionally you have to be aware that finalizers are not called when an object is no longer needed by your program. It is called when the garbage collector decides to remove the object. Usually you cannot predict when that will happen. Therefore you have to do a little bit more to provide a proper cleanup mechanism in your programs.

Take a look at the following program:

using System;
using System.IO;

namespace Finalizers
{
internal class FileGenerator : IDisposable
{
public FileGenerator()
{
}
~FileGenerator()
{
// Just a debug output
Console.WriteLine("Closing file!");
}
public void Generate(int Length)
{
// Here some work is done...
}
public void Dispose()
{
// Just a debug output
Console.WriteLine("Disposing object!");
}
}

class Program
{
static void Generate()
{
using ( var fGen = new FileGenerator() )
fGen.Generate(512);
}
static void Main(string[] args)
{
Generate();
// Here we do some work; simulated by ReadLine statement
Console.Write("Please Press Enter...");
Console.ReadLine();
}
}
}

If you run this program you will notice that the output Closing file! appears at the very end of the program. The destructor is not immediately called when the object fGen is out of scope. The CLR calls it when the garbage collector frees the object.

This is one of the reasons why a destructor or a finalizer is not enough for an object that has to clean up when it is no longer needed. Here is a list of things you should consider regarding cleanup:

Jeffrey Richter wrote an interesting article about memory management and finalization  of objects in the MSDN Magazine. Although the text has been written in 2000 it is still worth reading!

  1. Try to avoid objects that require clean up!
  2. If you cannot avoid it provide a destructor or a finalizer.
  3. Add an additional method with which a user can  force the object to clean up.
    1. Implement the IDisposable interface by providing a Dispose-method. Follow the guidelines for implementing IDisposable  that are included in MSDN.
    2. If a user can (re)open the object using some kind of Open-method offer aClose-method in addition to Dispose. The Close-method should call Disposeinternally.
    3. Do not forget to call SuppressFinalize in your Dispose-method. Otherwise you would waste performance.
  4. Objects that implement IDisposable  should be used inside C# using  statement. It automatically cleans up correctly.

Question 6 - Lambda Expressions

In C# every method must have a name. True or false?

That's not true! Since the version 2.0 C# has known anonymous methods. Take a look at the following code:

namespace AnonymousMethods
{
class Program
{
delegate int CalcOperation(int x);
static int PerformCalculation( int x, CalcOperation calc )
{
return calc(x);
}
static void Main()
{
System.Console.Write(PerformCalculation(5,
delegate(int x) { return x * x; }));
}
}
}

The main-method creates an anonymous method that is passed in the calc-parameter to thePerformCalculation-method. This construct is quite useful in a situation when having to create a method might seem an unnecessary overhead.

Read more about Lambda Expressions in MSDN.

In C# 3.0 you can replace anonymous methods with Lamdba Expressions in most cases. In our case we could change the implementation of the Main-method as follows:

static void Main()
{
System.Console.Write(PerformCalculation(5, x => x*x ));
}

If you take a look behind the scenes and check the intermediate language that the C# compiler generates you will see that the generated IL nearly does not differ between C# 2.0 anonymous methods and Lamdba Expressions.

Question 7 - Nullable Types

What is the output of the following program?

namespace NullableTypes
{
class Program
{
static void Main()
{
int a = 10;
int? b = 20;
int? c = null;
System.Console.WriteLine( a + c ?? b );
}
}
}

The correct answer is 20.

In C# you can make every value type nullable by adding a question mark to the type's name (you could also use System.Nullable<T> instead of the ?). The operator ?? can be used to specify a default value for an expression. This default value is returned if the expression evaluates to null. In our case c is null; therefore a + c is null and the default value b (20) is returned.

Question 8 - JIT Compiler

The JIT compiler converts the IL in a portable executable into native machine language...

The correct answer is method by method.

During loading the CLR creates a stub for each method in a type when it is loaded and initialized. When a method is called for the first time the stub passes control to the JIT compiler which converts the MSIL for that method into native code. If the method is called again the native code is executed without involving the JIT.

You can observe how the JITter generates native code using the following code sample:

Observe the number of methods Jited using Performance Monitor In the Performance Monitor you can see how the JITter generates native code.

namespace JIT
{
class Program
{
static void Method1()
{
System.Console.WriteLine("Method1");
}
static void Method2()
{
System.Console.WriteLine("Method2");
}
static void Main()
{
System.Console.ReadLine();
Method1();
System.Console.ReadLine();
Method2();
System.Console.ReadLine();
Method1();
System.Console.ReadLine();
}
}
}

Note: You cannot use ngen with ASP.NET (for details see Microsoft Support Site).

If you do not want to convert your IL code method by method every time the program starts you can use the Native Image Generator (ngen.exe) to convert it e.g. during installation. ngen generates processor-specific machine code in the native image cache. The runtime uses these images from the cache instead of using the JIT compiler to compile the original assembly.