Advance Topics in DotNet for Interview - part II

How do I spawn a thread?

Answer: Create an instance of a System.Threading.Thread object, passing it an instance of a ThreadStart delegate that will be executed on the new thread. For example:

class MyThread
{
public MyThread( string initData )
{
m_data = initData;
m_thread = new Thread( new ThreadStart(ThreadMain) ); 
m_thread.Start(); 
}

// ThreadMain() is executed on the new thread.
private void ThreadMain()

Console.WriteLine( m_data );
}

public void WaitUntilFinished()
{
m_thread.Join();


private Thread m_thread;
private string m_data;
}

In this case creating an instance of the MyThread class is sufficient to spawn the thread and execute the MyThread.ThreadMain() method:

MyThread t = new MyThread( "Hello, world." );
t.WaitUntilFinished();

How do I stop a thread?

Answer: There are several options. First, we can use our own communication mechanism to tell the ThreadStart method to finish. Alternatively the Thread class has in-built support for instructing the thread to stop. The two principle methods are Thread.Interrupt() and Thread.Abort(). The former will cause a ThreadInterruptedException to be thrown on the thread when it next goes into a WaitJoinSleep state. In other words, Thread.Interrupt is a polite way of asking the thread to stop when it is no longer doing any useful work. In contrast, Thread.Abort() throws a ThreadAbortException regardless of what the thread is doing. Furthermore, the ThreadAbortException cannot normally be caught (though the ThreadStart's finally method will be executed). Thread.Abort() is a heavy-handed mechanism which should not normally be required.

How do we use the thread pool?

Answer: By passing an instance of a WaitCallback delegate to the ThreadPool.QueueUserWorkItem() method

class CApp
{
static void Main()
{
string s = "Hello, World";
ThreadPool.QueueUserWorkItem( new WaitCallback( DoWork ), s );

Thread.Sleep( 1000 ); // Give time for work item to be executed
}

// DoWork is executed on a thread from the thread pool.
static void DoWork( object state )
{
Console.WriteLine( state );
}
}

How do we know when our thread pool work item has completed?

Answer: There is no way to query the thread pool for this information. We must put code into the WaitCallback method to signal that it has completed. Events are useful for this.

How do we prevent concurrent access to our data?

Answer: Each object has a concurrency lock (critical section) associated with it. The System.Threading.Monitor.Enter/Exit methods are used to acquire and release this lock. For example, instances of the following class only allow one thread at a time to enter method f():

class C
{
public void f()
{
try
{
Monitor.Enter(this);
...
}
finally
{
Monitor.Exit(this);
}
}
}
C# has a 'lock' keyword which provides a convenient shorthand for the code above:
class C
{
public void f()
{
lock(this)
{
...
}
}
}

Note that calling Monitor.Enter(myObject) does NOT mean that all access to myObject is serialized. It means that the synchronisation lock associated with myObject has been acquired, and no other thread can acquire that lock until Monitor.Exit(o) is called. In other words, this class is functionally equivalent to the classes above:

class C
{
public void f()
{
lock( m_object )
{
...
}
}

private m_object = new object();
}

Actually, it could be argued that this version of the code is superior, as the lock is totally encapsulated within the class, and not accessible to the user of the object. 

Should we use ReaderWriterLock instead of Monitor.Enter/Exit?

Answer: Maybe, but be careful. ReaderWriterLock is used to allow multiple threads to read from a data source, while still granting exclusive access to a single writer thread. This makes sense for data access that is mostly read-only, but there are some caveats. First, ReaderWriterLock is relatively poor performing compared to Monitor.Enter/Exit, which offsets some of the benefits. Second, we need to be very sure that the data structures you are accessing fully support multithreaded read access. Finally, there is apparently a bug in the v1.1 ReaderWriterLock that can cause starvation for writers when there are a large number of readers. 

Is there built-in support for tracing/logging?

Answer: Yes, in the System.Diagnostics namespace. There are two main classes that deal with tracing - Debug and Trace. They both work in a similar way - the difference is that tracing from the Debug class only works in builds that have the DEBUG symbol defined, whereas tracing from the Trace class only works in builds that have the TRACE symbol defined. Typically this means that you should use System.Diagnostics.Trace.WriteLine for tracing that you want to work in debug and release builds, and System.Diagnostics.Debug.WriteLine for tracing that you want to work only in debug builds.

Can we redirect tracing to a file?

Answer: Yes. The Debug and Trace classes both have a Listeners property, which is a collection of sinks that receive the tracing that you send via Debug.WriteLine and Trace.WriteLine respectively. By default the Listeners collection contains a single sink, which is an instance of the DefaultTraceListener class. This sends output to the Win32 OutputDebugString() function and also the System.Diagnostics.Debugger.Log() method. This is useful when debugging, but if we're trying to trace a problem at a customer site, redirecting the output to a file is more appropriate. Fortunately, the TextWriterTraceListener class is provided for this purpose.
Note the use of Trace.Listeners.Clear() to remove the default listener. If we don't do this, the output will go to the file and OutputDebugString(). Typically this is not what we want, because OutputDebugString() imposes a big performance hit.

Can we customise the trace output?

Answer: Yes. We can write our own TraceListener-derived class, and direct all output through it. 

Are there any third party logging components available?

Answer: Log4net is a port of the established log4j Java logging component.

How does .NET remoting work?

Answer: .NET remoting involves sending messages along channels. Two of the standard channels are HTTP and TCP. TCP is intended for LANs only - HTTP can be used for LANs or WANs (internet).

Support is provided for multiple message serializarion formats. Examples are SOAP (XML-based) and binary. By default, the HTTP channel uses SOAP (via the .NET runtime Serialization SOAP Formatter), and the TCP channel uses binary (via the .NET runtime Serialization Binary Formatter). But either channel can use either serialization format.

There are a number of styles of remote access:
· SingleCall. Each incoming request from a client is serviced by a new object. The object is thrown away when the request has finished. 
· Singleton. All incoming requests from clients are processed by a single server object. 
· Client-activated object. This is the old stateful (D)COM model whereby the client receives a reference to the remote object and holds that reference (thus keeping the remote object alive) until it is finished with it.

Distributed garbage collection of objects is managed by a system called 'leased based lifetime'. Each object has a lease time, and when that time expires the object is disconnected from the .NET runtime remoting infrastructure. Objects have a default renew time - the lease is renewed when a successful call is made from the client to the object. The client can also explicitly renew the lease.

How can we get at the Win32 API from a .NET program?

Answer: Use P/Invoke. This uses similar technology to COM Interop, but is used to access static DLL entry points instead of COM objects. Here is an example of C# calling the Win32 MessageBox function:

using System; 
using System.Runtime.InteropServices; 

class MainApp 

[DllImport("user32.dll", EntryPoint="MessageBox", SetLastError=true, CharSet=CharSet.Auto)] 
public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType);

public static void Main() 
{
MessageBox( 0, "Hello, this is PInvoke in operation!", ".NET", 0 ); 
}


What is the difference between an event and a delegate?

Answer: An event is just a wrapper for a multicast delegate. Adding a public event to a class is almost the same as adding a public multicast delegate field. In both cases, subscriber objects can register for notifications, and in both cases the publisher object can send notifications to the subscribers. However, a public multicast delegate has the undesirable property that external objects can invoke the delegate, something we'd normally want to restrict to the publisher. Hence events - an event adds public methods to the containing class to add and remove receivers, but does not make the invocation mechanism public. 

What size is a .NET object?

Answer: Each instance of a reference type has two fields maintained by the runtime - a method table pointer and a sync block. These are 4 bytes each on a 32-bit system, making a total of 8 bytes per object overhead. Obviously the instance data for the type must be added to this to get the overall size of the object. So, for example, instances of the following class are 12 bytes each:

class MyInt
{
...
private int x;
}

However, note that with the current implementation of the CLR there seems to be a minimum object size of 12 bytes, even for classes with no data (e.g. System.Object).

Values types have no equivalent overhead.

Will .NET app run on 64-bit Windows?

Answer: 64-bit (x64) versions of Windows support both 32-bit and 64-bit processes, and corresponding 32-bit and 64-bit versions of .NET 2.0. (.NET 1.1 is 32-bit only).
.NET 1.x apps automatically run as 32-bit processes on 64-bit Windows.
.NET 2.0 apps can either run as 32-bit processes or as 64-bit processes. The OS decides which to use based on the PE header of the executable. The flags in the PE header are controlled via the compiler /platform switch, which allows the target of the app to be specified as 'x86', 'x64' or 'any cpu'. Normally we specify 'any cpu', and our app will run as 32-bit on 32-bit Windows and 64-bit on 64-bit Windows. However if we have some 32-bit native code in our app (loaded via COM interop, for example), we will need to specify 'x86', which will force 64-bit Windows to load our app in a 32-bit process. We can also tweak the 32-bit flag in the PE header using the SDK corflags utility. 

What are the new features of .NET 2.0?

Answer: Generics, anonymous methods, partial classes, iterators, property visibility (separate visibility for get and set) and static classes.

What are the new 2.0 features useful for?

Answer: Generics are useful for writing efficient type-independent code, particularly where the types might include value types. The obvious application is container classes, and the .NET 2.0 class library includes a suite of generic container classes in the System.Collections.Generic namespace. Here's a simple example of a generic container class being used:

List<int> myList = new List<int>();
myList.Add( 10 );


Anonymous methods reduce the amount of code you have to write when using delegates, and are therefore especially useful for GUI programming. Here's an example

AppDomain.CurrentDomain.ProcessExit += delegate { Console.WriteLine("Process ending ..."); };

Partial classes is a useful feature for separating machine-generated code from hand-written code in the same class, and will therefore be heavily used by development tools such as Visual Studio. 

Iterators reduce the amount of code you need to write to implement IEnumerable/IEnumerator. Here's some sample code:

static void Main()
{
RandomEnumerator re = new RandomEnumerator( 5 );
foreach( double r in re )
Console.WriteLine( r );
Console.Read();
}

class RandomEnumerator : IEnumerable<double>
{
public RandomEnumerator(int size) { m_size = size; }

public IEnumerator<double> GetEnumerator()
{
Random rand = new Random();
for( int i=0; i < m_size; i++ )
yield return rand.NextDouble();
}

int m_size = 0;
}

The use of 'yield return' is rather strange at first sight. It effectively synthethises an implementation of IEnumerator, something we had to do manually in .NET 1.x.

What's the problem with .NET generics?

Answer: .NET generics work great for container classes. But what about other uses? Well, it turns out that .NET generics have a major limitation - they require the type parameter to be constrained. For example, you cannot do this:

static class Disposer<T>
{
public static void Dispose(T obj) { obj.Dispose(); }
}

The C# compiler will refuse to compile this code, as the type T has not been constrained, and therefore only supports the methods of System.Object. Dispose is not a method on System.Object, so the compilation fails. To fix this code, we need to add a where clause, to reassure the compiler that our type T does indeed have a Dispose method

static class Disposer<T> where T : IDisposable
{
public static void Dispose(T obj) { obj.Dispose(); }
}

The problem is that the requirement for explicit contraints is very limiting. We can use constraints to say that T implements a particular interface, but we can't dilute that to simply say that T implements a particular method. Contrast this with C++ templates (for example), where no constraint at all is required - it is assumed (and verified at compile time) that if the code invokes the Dispose() method on a type, then the type will support the method. 

In fact, after writing generic code with interface constraints, we quickly see that we haven't gained much over non-generic interface-based programming. For example, we can easily rewrite the Disposer class without generics:

static class Disposer
{
public static void Dispose( IDisposable obj ) { obj.Dispose(); }
}

What's new in the .NET 2.0 class library?

Answer: Here is a selection of new features in the .NET 2.0 class library:
· Generic collections in the System.Collections.Generic namespace. 
· The System.Nullable<T> type. (Note that C# has special syntax for this type, e.g. int? is equivalent to Nullable<int>) 
· The GZipStream and DeflateStream classes in the System.IO.Compression namespace. 
· The Semaphore class in the System.Threading namespace. 
· Wrappers for DPAPI in the form of the ProtectedData and ProtectedMemory classes in the System.Security.Cryptography namespace. 
· The IPC remoting channel in the System.Runtime.Remoting.Channels.Ipc namespace, for optimised intra-machine communication.

What is C#?

Answer: C# is a programming language designed by Microsoft. It is loosely based on C/C++, and bears a striking similarity to Java. Microsoft describe C# as follows:

"C# is a simple, modern, object oriented, and type-safe programming language derived from C and C++. C# (pronounced 'C sharp') is firmly planted in the C and C++ family tree of languages, and will immediately be familiar to C and C++ programmers. C# aims to combine the high productivity of Visual Basic and the raw power of C++."

How do we develop C# apps?

Answer: The (free) .NET SDK contains the C# command-line compiler (csc.exe). Visual Studio has fully integrated support for C# development. On Linux we can use Mono.

Does C# replace C++?

Answer: There are three options open to the Windows developer from a C++ background:
· Stick with standard C++. Don't use .NET at all. 
· Use C++ with .NET. Microsoft supply a .NET C++ compiler that produces IL rather than machine code. However to make full use of the .NET environment (e.g. garbage collection), a set of extensions are required to standard C++. In .NET 1.x this extended language is called Managed Extensions for C++. In .NET 2.0 ME C++ has been completely redesigned and renamed C++/CLI. 
· Forget C++ and use C#.
Each of these options has merits, depending on the developer and the application. ME C++ (soon to be C++/CLI) is very useful for interop between new .NET code and old C++ code - simply write a managed wrapper class using ME C++, then use the managed class from C#. From experience, this works well.

Does C# have its own class library?

Answer: Not exactly. The .NET Framework has a comprehensive class library, which C# can make use of. C# does not have its own class library.

What standard types does C# use?

Answer: C# supports a very similar range of basic types to C++, including int, long, float, double, char, string, arrays, structs and classes. However, don't assume too much. The names may be familiar, but many of the details are different. For example, a long is 64 bits in C#, whereas in C++ the size of a long depends on the platform (typically 32 bits on a 32-bit platform, 64 bits on a 64-bit platform). Also classes and structs are almost the same in C++ - this is not true for C#. Finally, chars and strings in .NET are 16-bit (Unicode/UTF-16), not 8-bit like C++.

Is it true that all C# types derive from a common base class?

Answer: Yes and no. All types can be treated as if they derive from object (System.Object), but in order to treat an instance of a value type (e.g. int, float) as object-derived, the instance must be converted to a reference type using a process called 'boxing'. In theory a developer can forget about this and let the run-time worry about when the conversion is necessary, but in reality this implicit conversion can have side-effects that may trip up the unwary.

So I can pass an instance of a value type to a method that takes an object as a parameter?

Answer: Yes. For example:

class CApplication
{
public static void Main()

int x = 28; 
string s = "SKP"; 

DisplayMe( x ); 
DisplayMe( s ); 


static void DisplayMe( object o ) 

System.Console.WriteLine( "You are {0}", o ); 
}


This would display:
You are 28
You are SKP

What are the fundamental differences between value types and reference types?

Answer: C# divides types into two categories - value types and reference types. Most of the intrinsic types (e.g. int, char) are value types. Structs are also value types. Reference types include classes, arrays and strings. The basic idea is straightforward - an instance of a value type represents the actual data, whereas an instance of a reference type represents a pointer or reference to the data.

The most confusing aspect of this for C++ developers is that C# has predetermined which types are represented as values, and which are represented as references. A C++ developer expects to take responsibility for this decision.

For example, in C++ we can do this:
int x1 = 3; // x1 is a value on the stack
int *x2 = new int(3) // x2 is a pointer to a value on the heap
but in C# there is no control:
int x1 = 3; // x1 is a value on the stack
int x2 = new int(); 
x2 = 3; // x2 is also a value on the stack!

So an int is a value type, and a class is a reference type. How can int be derived from object?

Answer: It isn't, really. When an int is being used as an int, it is a value. However, when it is being used as an object, it is a reference to an integer value (on the managed heap). In other words, when you treat an int as an object, the runtime automatically converts the int value to an object reference. This process is called boxing. The conversion involves copying the int to the heap, and creating an object instance which refers to it. Unboxing is the reverse process - the object is converted back to a value.

int x = 3; // new int value 3 on the stack
object objx = x; // new int on heap, set to value 3 - still have x=3 on stack
int y = (int)objx; // new value 3 on stack, still got x=3 on stack and objx=3 on heap

Are C# references the same as C++ references?

Answer: Not quite. The basic idea is the same, but one significant difference is that C# references can be null . So we cannot rely on a C# reference pointing to a valid object. In that respect a C# reference is more like a C++ pointer than a C++ reference. If we try to use a null reference, a NullReferenceException is thrown.
For example, look at the following method:

void displayStringLength( string s )
{
Console.WriteLine( "String is length {0}", s.Length );

The problem with this method is that it will throw a NullReferenceException if called like this:
string s = null;
displayStringLength( s );

Of course for some situations you may deem a NullReferenceException to be a perfectly acceptable outcome, but in this case it might be better to re-write the method like this:

void displayStringLength( string s )
{
if( s == null )
Console.WriteLine( "String is null" );
else
Console.WriteLine( "String is length {0}", s.Length );


Can we use typedefs in C#?

Answer: No, C# has no direct equivalent of the C++ typedef. C# does allow an alias to be specified via the using keyword:
using IntList = System.Collections.Generic.List<int>;

but the alias only applies in the file in which it is declared. A workaround in some cases is to use inheritance:
public class IntList : List<int> { }

Structs are largely redundant in C++. Why does C# have them?

Answer: In C++, a struct and a class are pretty much the same thing. The only difference is the default visibility level (public for structs, private for classes). However, in C# structs and classes are very different. In C#, structs are value types (instances stored directly on the stack, or inline within heap-based objects), whereas classes are reference types (instances stored on the heap, accessed indirectly via a reference). Also structs cannot inherit from structs or classes, though they can implement interfaces. Structs cannot have destructors. A C# struct is much more like a C struct than a C++ struct.

Does C# support multiple inheritance (MI)?

Answer: No, though it does support implementation of multiple interfaces on a single class or struct.

Is a C# interface the same as a C++ abstract class?

Answer: No, not quite. An abstract class in C++ cannot be instantiated, but it can (and often does) contain implementation code and/or data members. A C# interface cannot contain any implementation code or data members - it is simply a group of method names & signatures. A C# interface is more like a COM interface than a C++ abstract class.

Are C# constructors the same as C++ constructors?

Answer: Very similar, but there are some significant differences. First, C# supports constructor chaining. This means one constructor 

can call another:
class Person
{
public Person( string name, int age ) { ... }
public Person( string name ) : this( name, 0 ) {}
public Person() : this( "", 0 ) {}
}


Another difference is that virtual method calls within a constructor are routed to the most derived implementation 
Error handling is also somewhat different. If an exception occurs during construction of a C# object, the destuctor (finalizer) will still be called. This is unlike C++ where the destructor is not called if construction is not completed.

Finally, C# has static constructors. The static constructor for a class runs before the first instance of the class is created.
Also note that (like C++) some C# developers prefer the factory method pattern over constructors. 

Are C# destructors the same as C++ destructors?

Answer: No. They look the same but they are very different. The C# destructor syntax (with the familiar ~ character) is just syntactic sugar for an override of the System.Object Finalize method. This Finalize method is called by the garbage collector when it determines that an object is no longer referenced, before it frees the memory associated with the object. So far this sounds like a C++ destructor. The difference is that the garbage collector makes no guarantees about when this procedure happens. Indeed, the algorithm employed by the CLR garbage collector means that it may be a long time after the application has finished with the object. This lack of certainty is often termed 'non-deterministic finalization', and it means that C# destructors are not suitable for releasing scarce resources such as database connections, file handles etc.

To achieve deterministic destruction, a class must offer a method to be used for the purpose. The standard approach is for the class to implement the IDisposable interface. The user of the object must call the Dispose() method when it has finished with the object. C# offers the 'using' construct to make this easier.

If C# destructors are so different to C++ destructors, why did MS use the same syntax?

Answer: Presumably they wanted C++ programmers to feel at home. 

Are all methods virtual in C#?

Answer: No. Like C++, methods are non-virtual by default, but can be marked as virtual.

How do we declare a pure virtual function in C#?

Answer: Use the abstract modifier on the method. The class must also be marked as abstract (naturally). Note that abstract methods cannot have an implementation (unlike pure virtual C++ methods).

Can we call a virtual method from a constructor/destructor?

Answer: Yes, but it's generally not a good idea. The mechanics of object construction in .NET are quite different from C++, and this affects virtual method calls in constructors. 

C++ constructs objects from base to derived, so when the base constructor is executing the object is effectively a base object, and virtual method calls are routed to the base class implementation. By contrast, in .NET the derived constructor is executed first, which means the object is always a derived object and virtual method calls are always routed to the derived implementation. (Note that the C# compiler inserts a call to the base class constructor at the start of the derived constructor, thus preserving standard OO semantics by creating the illusion that the base constructor is executed first.)

The same issue arises when calling virtual methods from C# destructors. A virtual method call in a base destructor will be routed to the derived implementation.

Should we make my destructor virtual?

Answer: A C# destructor is really just an override of the System.Object Finalize method, and so is virtual by definition.

Can we use exceptions in C#?

Answer: Yes, in fact exceptions are the recommended error-handling mechanism in C# (and in .NET in general). Most of the .NET framework classes use exceptions to signal errors.

What types of object can we throw as exceptions?

Answer: Only instances of the System.Exception classes, or classes derived from System.Exception. This is in sharp contrast with C++ where instances of almost any type can be thrown.

Can we define our own exceptions?

Answer: Yes, just derive your exception class from System.Exception.

Does the System.Exception class have any cool features?

Answer: Yes - the feature which stands out is the StackTrace property. This provides a call stack which records where the exception was thrown from. For example, the following code:

using System;

class CApp 

public static void Main() 

try 

f(); 

catch( Exception e ) 

Console.WriteLine( "System.Exception stack trace = \n{0}", e.StackTrace ); 
}


static void f() 

throw new Exception( "f went pear-shaped" ); 

}
produces this output:
System.Exception stack trace = 
at CApp.f() 
at CApp.Main()

Note, however, that this stack trace was produced from a debug build. A release build may optimise away some of the method calls which could mean that the call stack isn't quite what you expect.

When should we throw an exception?

Answer: This is the subject of some debate, and is partly a matter of taste. However, it is accepted by many that exceptions should be thrown only when an 'unexpected' error occurs. How do you decide if an error is expected or unexpected? This is a judgement call, but a straightforward example of an expected error is failing to read from a file because the seek pointer is at the end of the file, whereas an example of an unexpected error is failing to allocate memory from the heap.

Does C# have a 'throws' clause?

Answer: No, unlike Java, C# does not require (or even allow) the developer to specify the exceptions that a method can throw.

How can we check the type of an object at runtime?

Answer: We can use the is keyword. For example:

using System; 

class CApp
{
public static void Main()

string s = "SKP"; 
long i = 10; 

Console.WriteLine( "{0} is {1}an integer", s, (IsInteger(s) ? "" : "not ") ); 
Console.WriteLine( "{0} is {1}an integer", i, (IsInteger(i) ? "" : "not ") ); 
}

static bool IsInteger( object obj )

if( obj is int || obj is long )
return true; 
else 
return false;
}


produces the output:
SKP is not an integer 
10 is an integer

Can we get the name of a type at runtime?

Answer: Yes, use the GetType method of the object class (which all types inherit from). For example:
using System; 

class CTest
{
class CApp 
{
public static void Main()

long i = 10; 
CTest ctest = new CTest(); 

DisplayTypeInfo( ctest ); 
DisplayTypeInfo( i ); 
}

static void DisplayTypeInfo( object obj ) 

Console.WriteLine( "Type name = {0}, full type name = {1}", obj.GetType(), obj.GetType().FullName ); 
}
}
}

produces the following output:
Type name = CTest, full type name = CTest 
Type name = Int64, full type name = System.Int64

---------------- Part-II end ------------

About the Author



Sunil Kr. Pandey

Asst. Professor
Department of Computer Science
School of Management Sciences(SMS)
Varanasi (UP) ,India.
E-mail : skphind@yahoo.co.uk




Added on April 28, 2008 Comment

Comments

#1

arthiga commented, on July 20, 2008 at 12:34 p.m.:

its very useful for me and the college students...
i need the java technical questions like this...
can u help me..

Post a comment

Your name:

Comment: