En PDF
En PDF
NET
1
ProblemBook.NET»
c 2014, Andrey Akinshin <[email protected]>
Edited by Ivan Pashchenko
2
Contents
Introduction 4
Problems 6
1 OOP (Problems) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2 LINQ (Problems) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3 Mathematics (Problems) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4 Value types (Problems) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5 Strings (Problems) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
6 Multithreading (Problems) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Solutions 24
7 OOP (Solutions) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
8 LINQ (Solutions) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
9 Mathematics (Solutions) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
10 Value types (Solutions) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
11 Strings (Solutions) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
12 Multithreading (Solutions) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Bibliography 41
3
Introduction
This book is a collection of problems on the knowledge of the .NET platform and the C# pro-
gramming language. To avoid misunderstandings, just want to say that the book is not:
• This book is not a universal way to check your knowledge platform .NET. If you are easily
solved all problems, it does not mean that you is a wonderful .NET-programmer. And if you
met a lot of new things for yourself, then it does not mean that you don’t know .NET.
• This book is not a compilation of new, previously unseen problems. Many examples can be
found in the literature, in questions on StackOverflow, in programmer’s blogs. Just because
they have become classics.
• This book is not focused on those who already feel Senior .NET Developer and want to learn
a lot.
So what is this book? ProblemBook.NET is an attempt to gather in one place a variety of interesting
practical problems on the knowledge platform. Problems are divided into chapters, so you can not
read everything, but only questions of those areas that are of interest to you. In this book you
will not find a deep philosophical questions such as "What is a class?" or "Why is polymorphism
needed?". Most of the problems is a piece of C#-code for which you should to determine the result.
Each question is provided with a solution (sometimes with a description of why .NET behaves this
way).
It is recommended to treat the problem is not as a way to test your knowledge as well as the
starting point for the discussion of various aspects of the platform. If you find something new for
yourself, it’s a great cause to learn .NET a little more detail. Try to play around with the code:
modify it and learn how changes affect the result. Read the relevant literature. Think about how
the new knowledge can be useful to you in your work.
ProblemBook.NET distributed electronically, the development is on GitHub. It should also be
noted that at present the problem book is far from the final version. The book will be updated with
new problems and old will be refined and improved.
Technical Details
Today, there are two popular platform implementation .NET: The original Microsoft .NET Frame-
work (hereinafter - MS.NET) and Mono. It is known that the behavior of some code fragments may
change when you change the implementation. Similarly, the behavior might depends on the CLR
or processor architecture. If the result of the fragment of the code depends on the environment,
it is likely that the answers will be given an explanation. However, it is not guaranteed. If one of
the examples you have received an answer that is different from this in the book, please contact the
author, that he corrected this mistake.
4
Examples of code in all tasks can be run from LINQPad (in C# Statements or C# Program,
depending on the availability of the method Main), but you should import the following namespace
(Query —> Query Properties —> Additional Namespace Imports):
System.Globalization
System.Runtime.InteropServices
System.Runtime.CompilerServices
Thanks
The author sincerely thanks the Chief Editor Ivan Pashchenko for useful discussions during the
writing of this book.
The author evinces appreciation to Ekaterina Demidova for the kindly providing illustrations.
5
Problems
1 OOP (Problems)
1. What will the following code display?
void Foo(object a)
{
Console.WriteLine("object");
}
void Foo(object a, object b)
{
Console.WriteLine("object, object");
}
void Foo(params object[] args)
{
Console.WriteLine("params object[]");
}
void Foo<T>(params T[] args)
{
Console.WriteLine("params T[]");
}
class Bar { }
void Main()
{
Foo();
Foo(null);
Foo(new Bar());
Foo(new Bar(), new Bar());
Foo(new Bar(), new object());
}
Solution
6
2. What will the following code display?
class Foo
{
public virtual void Quux(int a)
{
Console.WriteLine("Foo.Quux(int)");
}
}
class Bar : Foo
{
public override void Quux(int a)
{
Console.WriteLine("Bar.Quux(int)");
}
public void Quux(object a)
{
Console.WriteLine("Bar.Quux(object)");
}
}
class Baz : Bar
{
public override void Quux(int a)
{
Console.WriteLine("Baz.Quux(int)");
}
public void Quux<T>(params T[] a)
{
Console.WriteLine("Baz.Quux(params T[])");
}
}
void Main()
{
new Bar().Quux(42);
new Baz().Quux(42);
}
Solution
7
3. What will the following code display?
class Foo
{
public Foo()
{
Quux();
}
public virtual void Quux()
{
Console.WriteLine("Foo.Quux()");
}
}
class Bar : Foo
{
protected string name;
public Bar()
{
name = "Bar";
}
public override void Quux()
{
Console.WriteLine("Bar.Quux(), " + name);
}
public void Quux(params object[] args)
{
Console.WriteLine("Bar.Quux(params object[])");
}
}
class Baz : Bar
{
public Baz()
{
name = "Baz";
Quux();
((Foo)this).Quux();
}
}
void Main()
{
new Baz();
}
Solution
8
4. What will the following code display?
class Foo
{
protected class Quux
{
public Quux()
{
Console.WriteLine("Foo.Quux()");
}
}
}
class Bar : Foo
{
new class Quux
{
public Quux()
{
Console.WriteLine("Bar.Quux()");
}
}
}
class Baz : Bar
{
public Baz()
{
new Quux();
}
}
void Main()
{
new Baz();
}
Solution
9
5. What will the following code display?
class Foo<T>
{
public static int Bar;
}
void Main()
{
Foo<int>.Bar++;
Console.WriteLine(Foo<double>.Bar);
}
Solution
2 LINQ (Problems)
1. What will the following code display?
Solution
10
2. What will the following code display?
IEnumerable<string> Foo()
{
yield return "Bar";
Console.WriteLine("Baz");
}
void Main()
{
foreach (var str in Foo())
Console.Write(str);
}
Solution
Solution
Solution
Solution
11
6. What will the following code display?
Solution
7. What will the following code display?
int Inc(int x)
{
Console.WriteLine("Inc: " + x);
return x + 1;
}
void Main()
{
var numbers = Enumerable.Range(0, 10);
var query =
(from number in numbers
let number2 = Inc(number)
where number2 % 2 == 0
select number2).Take(2);
foreach (var number in query)
Console.WriteLine("Number: " + number);
}
Solution
8. At what point will happen Exception?
Solution
12
9. At what point will happen Exception?
Solution
Solution
13
3 Mathematics (Problems)
1. What will the following code display?
Console.WriteLine(
"| Number | Round | Floor | Ceiling | Truncate | Format |");
foreach (var x in new[] { -2.9, -0.5, 0.3, 1.5, 2.5, 2.9 })
{
Console.WriteLine(string.Format(CultureInfo.InvariantCulture,
"| {0,6} | {1,5} | {2,5} | {3,7} | {4,8} | {0,6:N0} |",
x, Math.Round(x), Math.Floor(x),
Math.Ceiling(x), Math.Truncate(x)));
}
Solution
Solution
Solution
14
4. What will the following code display?
var zero = 0;
try
{
Console.WriteLine(42 / 0.0);
Console.WriteLine(42.0 / 0);
Console.WriteLine(42 / zero);
}
catch (DivideByZeroException)
{
Console.WriteLine("DivideByZeroException");
}
Solution
Solution
15
6. What will the following code display?
int a = 0;
int Foo()
{
a = a + 42;
return 1;
}
void Main()
{
a += Foo();
Console.WriteLine(a);
}
Solution
byte foo = 1;
dynamic bar = foo;
Console.WriteLine(bar.GetType());
bar += foo;
Console.WriteLine(bar.GetType());
Solution
16
4 Value types (Problems)
1. What will the following code display?
struct Foo
{
int value;
public override string ToString()
{
if (value == 2)
return "Baz";
return (value++ == 0) ? "Foo" : "Bar";
}
}
void Main()
{
var foo = new Foo();
Console.WriteLine(foo);
Console.WriteLine(foo);
object bar = foo;
object qux = foo;
object baz = bar;
Console.WriteLine(baz);
Console.WriteLine(bar);
Console.WriteLine(baz);
Console.WriteLine(qux);
}
Solution
17
2. What will the following code display?
Solution
var x = new
{
Items = new List<int> { 1, 2, 3 }.GetEnumerator()
};
while (x.Items.MoveNext())
Console.WriteLine(x.Items.Current);
Solution
18
4. What will the following code display?
Solution
5 Strings (Problems)
1. What will the following code display?
Console.WriteLine(1 + 2 + "A");
Console.WriteLine(1 + "A" + 2);
Console.WriteLine("A" + 1 + 2);
Solution
Console.WriteLine(1 + 2 + ’A’);
Console.WriteLine(1 + ’A’ + 2);
Console.WriteLine(’A’ + 1 + 2);
Solution
19
3. What will the following code display?
try
{
Console.WriteLine(((string)null + null + null) == null);
}
catch (Exception e)
{
Console.WriteLine(e.GetType());
}
Solution
Console.WriteLine(
string.Compare(a.ToUpper(), b.ToUpper()));
Console.WriteLine(
string.Compare(a, b, StringComparison.OrdinalIgnoreCase));
Solution
5. Please, append the following code so that it displays the console output contained the string
Foo, without redefining the standard method Console.WriteLine?
// ???
Console.WriteLine(-42);
Solution
Thread.CurrentThread.CurrentUICulture =
CultureInfo.CreateSpecificCulture("ru-RU");
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("en-US");
var dateTime = new DateTime(2014, 12, 31, 13, 1, 2);
Console.WriteLine(dateTime);
7. Can the two lines coincide by the String.CompareTo method, but differ by the String.Equals
method? How to contact them the ==?
Solution
20
8. Please, append the following code so that it displays the console output the aaaaa string,
without redefining the standard method Console.WriteLine?
// ???
Console.WriteLine("Hello");
Solution
var x = "AB";
var y = new StringBuilder().Append(’A’).Append(’B’).ToString();
var z = string.Intern(y);
Console.WriteLine(x == y);
Console.WriteLine(x == z);
Console.WriteLine((object)x == (object)y);
Console.WriteLine((object)x == (object)z);
Solution
[assembly: CompilationRelaxationsAttribute
(CompilationRelaxations.NoStringInterning)]
Does it means, that the literal string in this assembly now will be interned?
Solution
11. If the mechanism of internment lines under this version of the CLR is disabled and in a literal
line of code "Hello" occurs twice, how many times it will occur in the assembly metadata?
Solution
12. Keep in regular strings secret data (such as a password) is a bad way, since string is stored in
memory in the clear, hackers can easily get to it. What are the standard tools to protect data?
Solution
13. Suppose we have an array of different strings. Can the sorting method from time to time give
different results?
Solution
14. Can the number of text elements (which can be accessed via TextElementEnumerator) of string
differ from the number of char-forming her character?
Solution
21
6 Multithreading (Problems)
1. What will the following code display?
[ThreadStatic]
static readonly int Foo = 42;
void Main()
{
var thread = new Thread(() => Console.WriteLine(Foo));
thread.Start();
thread.Join();
}
Solution
22
2. What will the following code display?
class Foo
{
private readonly ReaderWriterLockSlim lockSlim;
~Foo()
{
Console.WriteLine("~Foo: start");
try
{
if (lockSlim != null)
lockSlim.ExitReadLock();
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.GetType().Name);
}
Console.WriteLine("~Foo: finish");
}
}
void Main()
{
var foo = new Foo(new ReaderWriterLockSlim());
GC.Collect();
GC.WaitForPendingFinalizers();
}
Solution
23
Solutions
7 OOP (Solutions)
1. Problem “Oop:OverloadResolutionBasic”
Answer
params object[]
params object[]
params T[]
params T[]
object, object
Explanation
There are methods:
The object and object, object options is inaccessible because of number of arguments. The
params T[] option is inaccessible because of the compiler can’t resolve T. Thus, the right
options is params object[].
The object, object option is inaccessible because of number of arguments. The params T[]
options is inaccessible because of the compiler can’t resolve T. The params object[] option is
acceptable in its expanded and unexpanded forms. In this case, we should choose the expanded
form, i.e. the method signature will look like Foo(object[] args). Now, we have the object
and object[] options. The compiler will choose more specific option, i.e. object[] (or
params object[]).
24
The object, object option is inaccessible because of number of arguments. The object
and params object[] options request additional implicitly conversion Bar to object. So, the
params T[] (or params Bar[]) option is more preferable.
The object, object option is inaccessible because of number of arguments. The object
and params object[] options request additional implicitly conversion Bar to object. So, the
params T[] (or params Bar[]) option is more preferable.
The object option is inaccessible because of number of arguments. There is exactly one option
without the params keyword: object, object. It is more preferable.
Links
2. Problem “Oop:OverloadResolutionOverride”
Answer
Bar.Quux(object)
Baz.Quux(params T[])
Explanation
There is a rule: if compiler found a suitable signature for a method call in the “current” class,
compiler will not look to parents classes. In this problem, the Bar and Baz classes have own
versions of the Quux method. Their signatures are suitable for the call argument list. Thus,
they will be called; the overloaded Quux method of the base class will be ignored.
Links
3. Problem “Oop:OverloadResolutionInheritance”
Answer
Bar.Quux(),
Bar.Quux(params object[])
Bar.Quux(), Baz
Explanation
A schematic calls tree of the code:
25
Main();
new Baz(); // Baz.ctor
base.ctor(); // Bar.ctor
base.ctor(); // Foo.ctor
Quux(); // Bar.Quux() beacuse there is an overload
Console.WriteLine("Bar.Quux(), " + name);
// name doesn’t have value
name = "Bar";
name = "Baz";
Quux();
// Bar.Quux(params object[] args) because Bar have a suitable method
Console.WriteLine("Bar.Quux(params object[])");
((Foo)this).Quux(); // Bar.Quux() beacuse there is an overload
Console.WriteLine("Bar.Quux(), " + name); // name == "Baz"
Links
4. Problem “Oop:InheritanceNestedClass”
Answer
Foo.Quux()
Explanation
The Bar.Quux class is declared in private scope, you can’t use it from a child class. Thus, the
call of the Quux method from the Baz class will be used the Foo.Quux class.
5. Problem “Oop:StaticFieldInGenericType”
Answer
Explanation
The Foo<int> and Foo<double> are two different classes. Each of them has own static Bar
field.
CSharp Language Specification 5.0, 10.5.1: “When a field declaration includes a static modifier,
the fields introduced by the declaration are static fields. When no static modifier is present,
the fields introduced by the declaration are instance fields. Static fields and instance fields are
two of the several kinds of variables (§5) supported by C#, and at times they are referred to
as static variables and instance variables, respectively. A static field is not part of a specific
instance; instead, it is shared amongst all instances of a closed type (§4.4.2). No matter how
many instances of a closed class type are created, there is only ever one copy of a static field
for the associated application domain.”
Links
26
• Microsoft Code Analysis for Managed Code Warnings, CA1000
• ReSharper wiki: Static field in generic type
8 LINQ (Solutions)
1. Problem “Linq:EnumerableToArray”
Answer
GetString: Foo
EnumerableToArray: Foo
GetString: Bar
EnumerableToArray: Bar
GetString: Foo
GetString: Bar
Explanation
LINQ queries use deferred/lazy execution. It means, a LINQ query without a cast method like
ToArray() or ToList() is not executed immediately. The execution will be deferred until we do
not explicitly require the results. Thus, the line
query execution will be performed. Moreover, first will be evaluated the first yield (print
GetString: Foo), next the loop body will be evaluated for the first enumerable element (print
EnumerableToArray: Foo). Next, the foreach loop will require the second enumerable el-
ement, the second yield will be evaluated (print GetString: Bar), the loop body will be
evaluated for the second element (print EnumerableToArray: Bar).
Next, the following line will be executed:
return strings.ToArray();
Our LINQ query will be performed again. So, the lines GetString: Foo and GetString: Bar
will be printed again.
2. Problem “Linq:LifeAfterYield”
Answer
BarBaz
27
Explanation
The code from the problem will be transformed in the following code (some part of the code
specifically removed for better understanding):
object IEnumerator.Current
{
get { return Current; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
bool IEnumerator.MoveNext()
{
switch (state)
{
case 0:
Current = "Bar";
state = 1;
return true;
case 1:
state = -1;
28
Console.WriteLine("Baz");
break;
}
return false;
}
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
}
}
IEnumerable<string> Foo()
{
return new FooEnumerable(-2);
}
void Main()
{
var enumerator = Foo().GetEnumerator();
while (enumerator.MoveNext())
Console.Write(enumerator.Current);
}
3. Problem “Linq:ClosureAndForeach”
Answer
In old compiler versions: 3 3 3.
In new compiler versions: 1 2 3.
Explanation
In old compiler versions, the code from the problem will be transformed in the following code:
29
}
Thus, all elements from the list refer to same delegate. So, we will see 3 same values in the
console, they will equal the last value of i.
Some breaking changes were in new compiler versions. The new version of the code:
Now, each element of the list refers to own delegate, all printed values will be different.
Examples:
30
MS compiler 4.0.30319.1 : 3 3 3
MS compiler 4.0.30319.33440 : 1 2 3
MS compiler 4.0.30319.33440 langversion=4 : 1 2 3
Links
• ItDepends.NET: ClosureAndForeach
• Eric Lippert: Closing over the loop variable considered harmful
• Eric Lippert: Closing over the loop variable, part two
4. Problem “Linq:ClosureAndFor”
Answer
3
3
3
5. Problem “Linq:QueryAfterRemove”
Answer
Explanation
When you call list.Where(c => c.StartsWith("B")), query will only built, but not exe-
cuted. The actual execution begins at the moment of the call query.Count(). By this time,
the value of list will be { "Foo", "Baz" }, and hence, there exists only one element that
starts with the ’B’ letter.
6. Problem “Linq:ClosureAndVariable”
Answer
Explanation
The code is translated into the following form:
class DisplayClass
{
public string startLetter;
31
public bool Method2(string c)
{
return c.StartsWith(this.startLetter);
}
}
void Main()
{
DisplayClass displayClass = new DisplayClass();
var list1 = new List<string> { "Foo", "Bar", "Baz" };
var list2 = list1;
displayClass.startLetter = "F";
IEnumerable<string> source = list2.Where(displayClass.Method1);
displayClass.startLetter = "B";
Console.WriteLine(source.Where(displayClass.Method2).Count());
}
The execution of the LINQ queries will start only in the last line of code. As can be seen, the
same helper class creates for both queries. First, the list2.Where(displayClass.Method1)
query will be executed. It returns { "Bar", "Baz" } because displayClass.startLetter at
the time of execution is "B". Next, the source.Where(displayClass.Method2) query will be
executed. It also returns { "Bar", "Baz" }. The number of elements in the result is two.
7. Problem “Linq:QueryWithInc”
Answer
Inc: 0
Inc: 1
Number: 2
Inc: 2
Inc: 3
Number: 4
Explanation
An imperative version of the code is as follows:
32
takenAmount++;
if (takenAmount == 2)
break;
}
}
8. Problem “Linq:ExceptionYieldYield”
Answer
Call of evenNumbers.FirstOrDefault().
Explanation
The lines
only build a query, but don’t execute it. A logic of the GetSmallNumbers() method starts run
at the first call of the MoveNext() method, which corresponds to the first call of the MoveNext()
method. At this point, the Exception will happen.
9. Problem “Linq:YieldExceptionYield”
Answer
Exception is not going to happen.
Explanation
Indeed, the line
is only building a query, but does not execute it. The line
is also only building another query without direct execution. Let’s look to the last list of the
Main method:
Console.WriteLine(evenNumbers.FirstOrDefault());
This call will get only the first element of the result enumerable (single call of MoveNext() and
Current will be executed). Evaluation of the next elements will nor occur. Thus, the code will
work without any exceptions.
33
Foo
1
Links
9 Mathematics (Solutions)
1. Problem “Math:Rounding1”
Answer
Explanation
If the number is exactly halfway between two possibilities, the following rules will work:
Links
2. Problem “Math:Rounding2”
Answer
34
a = b * (a/b) + (a%b)
7 = 3 * 2 + 1
7 = -3 * -2 + 1
-7 = 3 * -2 + -1
-7 = -3 * 2 + -1
3. Problem “Math:Eps”
Answe
4. Problem “Math:DivideByZero”
Answer
Infinity
Infinity
DivideByZeroException
5. Problem “Math:Overflow”
Answer
Overflow operations with sbyte, byte, short, ushort, int, uint, long, ulong, char throw
OverflowException depending on checked/unchecked context (ECMA-334, 11.1.5).
Overflow operations with float, double never throw OverflowException (ECMA-334, 11.1.6).
Overflow operations with decimal always throw OverflowException (ECMA-334, 11.1.7).
6. Problem “Math:AugmentedAssignment”
Answer
Explanation
The construction
a += Foo();
35
will be transformed to
a = a + Foo();
7. Problem “Math:DynamicIncrement”
Answer
System.Byte
System.Int32
Foo
Foo
Foo
Bar
Baz
Foo
2. Problem “ValueTypes:MutableProperty”
Answer
3. Problem “ValueTypes:Enumerator”
Answer
Loop hangs, zeros will be displayed.
4. Problem “ValueTypes:StructLayout”
Answer
8
8
36
11 Strings (Solutions)
1. Problem “Strings:PlusString”
Answer
3A
1A2
A12
2. Problem “Strings:PlusChar”
Answer
68
68
68
3. Problem “Strings:StringPlusNull”
Answer
False
Explanation
A text fragment from C# Language Specification, the “7.8.4 Addition operator” section:
“String concatenation:
Links
37
• Mono 3.10.0 Source
4. Problem “Strings:CaseInComparison”
Answer
No.
Explanation
It depends from current CultureInfo. An example:
var a = "i";
var b = "I";
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("tr-TR");
Console.WriteLine(
string.Compare(a.ToUpper(), b.ToUpper())); //’1’
Console.WriteLine(
string.Compare(a, b, StringComparison.OrdinalIgnoreCase)); //’0’
Links
5. Problem “Strings:CorruptedNumber”
Answer
You can use custom CultureInfo:
6. Problem “Strings:CurrentCulture”
Answer
.NET Runtime will be use CurrentCulture.
Explanation
Thus, the en-US locale will be used:
12/31/2014 1:01:02 PM
31.12.2014 13:01:02
38
7. Problem “Strings:CompareToVsEquals”
Answer
Yes.
Explanation
CompareTo executes according to CultureInfo, Equals — without.
An example: the string "ß" (Eszett , 0̆0df) and "ss" will be equal by CompareTo in German
CultreInfo.
The == operator works like the String.Equals method.
8. Problem “Strings:CorruptedString”
Answer
You can use the internment mechanism:
var s = "Hello";
string.Intern(s);
unsafe
{
fixed (char* c = s)
for (int i = 0; i < s.Length; i++)
c[i] = ’a’;
}
Console.WriteLine("Hello"); // Displays: "aaaaa"
9. Problem “Strings:ExplicitlyInternment”
Answer
True
True
False
True
39
12. Problem “Strings:Secure”
Answer
You should use SecureString.
Links
12 Multithreading (Solutions)
1. Problem “Multithreading:ThreadStaticVariable”
Answer
Links
• MSDN: ThreadStaticAttribute
• StackOverflow: How does the ThreadStatic attribute work?
2. Problem “Multithreading:LockSlimInFinalizer”
Answer
~Foo: start
Exception: SynchronizationLockException
~Foo: finish
Explanation
A SynchronizationLockException will be thrown because the finalizer will be called from the
GC thread.
40
Bibliography
10. «Under the Hood of .NET Memory Management» by Chris Farrell, Nick Harrison
11. «Effective C#: 50 Specific Ways to Improve Your C#» by Bill Wagner
14. «Essential .Net Volume 1: The Common Language Runtime» by Don Box
41