[go: up one dir, main page]

0% found this document useful (0 votes)
326 views5 pages

C#Brain Teasers

This document provides answers to 6 C# brainteaser questions. For the first question, it explains that when choosing an overload, if a derived class declares any compatible methods, signatures in the base class will be ignored. For the second question, it discusses the order of initialization for static fields and constructors. For the third question, it explains that floating point numbers are stored approximately in binary, so calculations may not equal exact values. For the fourth question, it describes how capturing variables in anonymous methods works. For the fifth and sixth questions, it discusses compiler bugs and type inference in overloading resolution.

Uploaded by

Nishant.kumarid
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
326 views5 pages

C#Brain Teasers

This document provides answers to 6 C# brainteaser questions. For the first question, it explains that when choosing an overload, if a derived class declares any compatible methods, signatures in the base class will be ignored. For the second question, it discusses the order of initialization for static fields and constructors. For the third question, it explains that floating point numbers are stored approximately in binary, so calculations may not equal exact values. For the fourth question, it describes how capturing variables in anonymous methods works. For the fifth and sixth questions, it discusses compiler bugs and type inference in overloading resolution.

Uploaded by

Nishant.kumarid
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 5

C# Brainteasers - Answers

These are the answers for the C# brainteasers. I've included the questions again just for
clarity.

1) Overloading
What is displayed, and why?

using System;

class Base
{
public virtual void Foo(int x)
{
Console.WriteLine ("Base.Foo(int)");
}
}

class Derived : Base


{
public override void Foo(int x)
{
Console.WriteLine ("Derived.Foo(int)");
}

public void Foo(object o)


{
Console.WriteLine ("Derived.Foo(object)");
}
}

class Test
{
static void Main()
{
Derived d = new Derived();
int i = 10;
d.Foo(i);
}
}

Answer: Derived.Foo(object) is printed - when choosing an overload, if there are any


compatible methods declared in a derived class, all signatures declared in the base class
are ignored - even if they're overridden in the same derived class!
2) Order! Order!
What will be displayed, why, and how confident are you?

using System;

class Foo
{
static Foo()
{
Console.WriteLine ("Foo");
}
}

class Bar
{
static int i = Init();

static int Init()


{
Console.WriteLine("Bar");
return 0;
}
}

class Test
{
static void Main()
{
Foo f = new Foo();
Bar b = new Bar();
}
}

Answer: On my box, Bar is printed and then Foo. This is because Foo has a static
constructor, which cannot be run until the exact point at which the class first has to be
initialized. Bar doesn't have a static constructor though, so the CLR is allowed to
initialize it earlier. However, there's nothing to guarantee that Bar will be printed at all.
No static fields have been referenced, so in theory the CLR doesn't have to initialize it at
all in our example. This is all due to the beforefieldinit flag.

3) Silly arithmetic
Computers are meant to be good at arithmetic, aren't they? Why does this print "False"?
double d1 = 1.000001;
double d2 = 0.000001;
Console.WriteLine((d1-d2)==1.0);

Answer: All the values here are stored as binary floating point. While 1.0 can be stored
exactly, 1.000001 is actually stored as
1.0000009999999999177333620536956004798412322998046875, and 0.000001 is
actually stored as
0.0000009999999999999999547481118258862586856139387236908078193664550781
25. The difference between them isn't exactly 1.0, and in fact the difference can't be
stored exactly either. Learn more about binary floating point

4) Print, print, print...


Here's some code using the anonymous method feature of C# 2. What does it do?

using System;
using System.Collections.Generic;

class Test
{
delegate void Printer();

static void Main()


{
List<Printer> printers = new List<Printer>();
for (int i=0; i < 10; i++)
{
printers.Add(delegate { Console.WriteLine(i); });
}

foreach (Printer printer in printers)


{
printer();
}
}
}

Answer: Ah, the joys of captured variables. There's only one i variable here, and its value
changes on each iteration of the loop. The anonymous methods capture the variable itself
rather than its value at the point of creation - so the result is 10 printed ten times!

5) Literally nothing wrong with the compiler here...


Should this code compile? Does it? What does it mean?
using System;

class Test
{
enum Foo { Bar, Baz };

static void Main()


{
Foo f = 0.0;
Console.WriteLine(f);
}
}

Answer: This shouldn't compile, but it does under the MS compilers for both C# 2 and 3
(and probably 1 as well - I haven't checked). It shouldn't compile because only the literal
0 should be implicitly convertible to the default value of any enum. Here the decimal is
0.0. Just a little compiler bug. The result is to print Bar as that's the 0 value of the Foo.

More along the same lines...

using System;

class Test
{
enum Foo { Bar, Baz };

const int One = 1;


const int Une = 1;

static void Main()


{
Foo f = One-Une;
Console.WriteLine(f);
}
}

Answer: Eek - it gets worse! This won't compile under the MS C# 2 compiler, but will
compile with the MS C# 3 compiler. It's a known bug due to some optimisation being
done too early, collecting constants of 0 and thinking that any known 0 constant should
be convertible to the 0 value of any enum. It's with us now, and unlikely to ever be fixed
as it could break some code which is technically illegal but working perfectly well. It's
possible that the spec will change instead, of course.

6) Type inference a-go-go


I first saw this on Ayende's blog (in a rather more obscure form, admittedly). Once again,
work out what will be printed, and why.

using System;

class Test
{
static void Main()
{
Foo("Hello");
}

static void Foo(object x)


{
Console.WriteLine("object");
}

static void Foo<T>(params T[] x)


{
Console.WriteLine("params T[]");
}
}

Answer: params T[] is printed. Now why would the compiler choose to create an array
when it doesn't have to? Well... there are two stages to this. Firstly, when trying to find
overloads which are legitimate candidates to be called, type inference works out that T
should be System.String. Nothing scary so far.

Then overloading tries to work out which method is "better". If it's a choice between
string x and params string[] x the former will always win - but at this point it's
effectively a choice between object x and params string[] x. (The fact that one is
actually a generic method is only relevant in a tie-break situation.) For the purposes of
working out "better conversions" the expanded form of the method with the params
parameter is then used. This means that by the time actual conversions are considered, the
choices are object x or string x - so clearly the latter wins.

You might also like