"YOU AND THE ART OF ONLINE DATING" is the only product on the market that will take you step-by-step through the process of online dating, provide you with the resources to help ensure success. Get it now!
I think I showed you this one before, but I like the fun here - this took some debugging to track down! (the original code was obviously more complex and subtle...) static void Foo() where T : new() { T t = new T(); Console. WriteLine(t.ToString()); // works fine Console.
WriteLine(t.GetHashCode()); // works fine Console. WriteLine(t. Equals(t)); // works fine // so it looks like an object and smells like an object... // but this throws a NullReferenceException... Console.
WriteLine(t.GetType()); } So what was T... Answer: any Nullable - such as int?. All the methods are overridden, except GetType() which can't be; so it is cast (boxed) to object (and hence to null) to call object.GetType()... which calls on null ;-p Update: the plot thickens... Ayende Rahien threw down a similar challenge on his blog, but with a where T : class, new(): private static void Main() { CanThisHappen(); } public static void CanThisHappen() where T : class, new() { var instance = new T(); // new() on a ref-type; should be non-null, then Debug. Assert(instance!
= null, "How did we break the CLR? "); } But it can be defeated! Using the same indirection used by things like remoting; warning - the following is pure evil: class MyFunnyProxyAttribute : ProxyAttribute { public override MarshalByRefObject CreateInstance(Type serverType) { return null; } } MyFunnyProxy class MyFunnyType : ContextBoundObject { } With this in place, the new() call is redirected to the proxy (MyFunnyProxyAttribute), which returns null.
Now go and wash your eyes!
– Drew Noakes Oct 23 '08 at 8:37 67 Drew: the problem is that GetType() isn't virtual, so it's not overridden - which means that the value is boxed for the method call. The box becomes a null reference, hence the NRE. – Jon Skeet Oct 25 '08 at 19:52 10 @Drew; additionally, there are special boxing rules for Nullable, which means that an empty Nullable boxes to null, not a box that contains an empty Nullable (and a null un-boxes to an empty Nullable) – Marc Gravell?
Oct 26 '08 at 8:34 29 Very, very cool.In an uncool kind of way. ;-) – Konrad Rudolph Oct 27 '08 at 21:33 6 Constructor-constraint, 10.1.5 in the C# 3.0 langauge spec – Marc Gravell? Nov 4 '09 at 16:39.
Bankers' Rounding. This one is not so much a compiler bug or malfunction, but certainly a strange corner case... The . Net Framework employs a scheme or rounding known as Banker's Rounding.In Bankers' Rounding the 0.5 numbers are rounded to the nearest even number, so Math.
Round(-0.5) == 0 Math. Round(0.5) == 0 Math. Round(1.5) == 2 Math.
Round(2.5) == 2 etc... This can lead to some unexpected bugs in financial calculations based on the more well known Round-Half-Up rounding. This is also true of Visual Basic.
20 It seemed strange to me too. That is, at least, until I had round a big list of numbers and calculate their sum. You then realize that if you simply round up, you will end up with potentially huge difference from the sum of the non-rounded numbers.
Very bad if you are doing financial calculations! – Tsvetomir Tsonev Oct 12 '08 at 10:05 245 In case people didn't know, you can do: Math. Round(x, MidpointRounding.
AwayFromZero); To change the rounding scheme. – ICR Oct 12 '08 at 12:24 26 From the docs: The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding.It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction.
– ICR Oct 12 '08 at 12:25 8 I wonder if this is why I see int(fVal + 0.5) so often even in languages which have a built-in rounding function. – Ben Blank Oct 12 '08 at 19:50 28 Ironically, I worked at a bank once and the other programmers started flipping out about this, thinking that rounding was broken in the framework – dan Oct 12 '08 at 20:43.
Static void Rec(int i) { Console. WriteLine(i); if (i MaxValue) { Rec(++i); } } Answer: On 32-bit JIT it should result in a StackOverflowException On 64-bit JIT it should print all the numbers to int. MaxValue This is because the 64-bit JIT compiler applies tail call optimisation, whereas the 32-bit JIT does not.
Unfortunately I haven't got a 64-bit machine to hand to verify this, but the method does meet all the conditions for tail-call optimisation. If anybody does have one I'd be interested to see if it's true.
10 Has to be compiled in release mode, but most definitely works on x64 =) – Neil Williams Oct 12 '08 at 18:43 3 might be worth updating your answer when VS 2010 comes out since all current JITs will then do the TCO in Release mode – ShuggyCoUk Jun 28 '09 at 23:40 3 Just tried on VS2010 Beta 1 on 32-bit WinXP. Still get a StackOverflowException. – squillman Aug 26 '09 at 20:45 3 Yeah, tail call support in the JIT is only useful if the compiler generates tail opcode prefixes, which it looks like the C# compiler still doesn't do.
The equivalent F# code should work perfectly, though. :) – bcat Sep 5 '09 at 4:03 114 +1 for the StackOverflowException – calvinlough Jan 5 '10 at 17:14.
Assign This! This is one that I like to ask at parties (which is probably why I don't get invited anymore): Can you make the following piece of code compile? Public void Foo() { this = new Teaser(); } An easy cheat could be: string cheat = @" public void Foo() { this = new Teaser(); } "; But the real solution is this: public struct Teaser { public void Foo() { this = new Teaser(); } } So it's a little know fact that value types (structs) can reassign their this variable.
3 C++ classes can do that too... as I discovered somewhat recently, only to be yelled at for actually trying to use it for an optimization :p – Mark Mar 1 '10 at 7:48 3 @Mark reassigning this in C++ is evil... I cant express how evil it is ...really... If you want to point this to a new object, manually call the destructor of the object, and then call in-place new, but keep in mind even this will fuck everything up, if the object this pointed to was a derived class of the class you just reconstructed... – smerlin Mar 12 '10 at 8:23 63 This is also a cheat: //this = new Teaser(); :-) – AndrewJacksonZA Mar 18 '10 at 11:06 16 :-) I'd prefer those cheats in my production code, than this reassignment abomination... – Omer Mor Mar 24 '10 at 21:49 2 From CLR via C#: The reason they made this is because you can call the parameterless constructor of a struct in another constructor. If you only want to initialize one value of a struct and want the other values to be zero/null (default), you can write public Foo(int bar){this = new Foo(); specialVar = bar;}. This is not efficient and not really justified (specialVar is assigned twice), but just FYI.(That's the reason given in the book, I don't know why we shouldn't just do public Foo(int bar) : this()) – kizzx2 Jan 30 at 17:40.
Few years ago, when working on loyality program, we had an issue with the amount of points given to customers. The issue was related to casting/converting double to int. In code below: double d = 13.6; int i1 = Convert.
ToInt32(d); int i2 = (int)d; does i1 == i2? It turns out that i1! = i2.
Because of different rounding policies in Convert and cast operator the actual values are: i1 == 14 i2 == 13 It's always better to call Math.Ceiling() or Math.Floor() (or Math. Round with MidpointRounding that meets our requirements) int i1 = Convert. ToInt32( Math.
Ceiling(d) ); int i2 = (int) Math. Ceiling(d).
40 Casting to an integer doesn't round, it just chops it off (effectively always rounding down). So this makes complete sense. – Max Schmeling May 14 '09 at 17:34 54 @Max: yes, but why does Convert round?
– Stefan Steinegger May 19 '09 at 11:07 15 @Stefan Steinegger If all it did was cast, there would be no reason for it in the first place, would it? Also note that the class name is Convert not Cast. – bug-a-lot Aug 4 '09 at 13:35 3 In VB: CInt() rounds.Fix() truncates.
Burned me once (blog.wassupy. Com/2006/01/i-can-believe-it-not-truncating. Html) – Michael Haren Aug 27 '09 at 20:56.
They should have made 0 an integer even when there's an enum function overload. I knew C# core team rationale for mapping 0 to enum, but still, it is not as orthogonal as it should be. Example from Npgsql.
Test example: namespace Craft { enum Symbol { Alpha = 1, Beta = 2, Gamma = 3, Delta = 4 }; class Mate { static void Main(string args) { JustTest(Symbol. Alpha); // enum JustTest(0); // why enum JustTest((int)0); // why still enum int I = 0; JustTest(Convert. ToInt32(0)); // have to use Convert.
ToInt32 to convince the compiler to make the call site use the object version JustTest(i); // it's ok from down here and below JustTest(1); JustTest("string"); JustTest(Guid.NewGuid()); JustTest(new DataTable()); Console.ReadLine(); } static void JustTest(Symbol a) { Console. WriteLine("Enum"); } static void JustTest(object o) { Console. WriteLine("Object"); } } }.
16 Wow that's a new one for me. Also wierd how ConverTo. ToIn32() works but casting to (int)0 doesn't.
And any other number > 0 works. (By "works" I mean call the object overload. ) – Lucas Jun 12 '09 at 18:52 1 @Chris Clark: I tried putting None = 0 on enum Symbol.
Still the compiler chooses enum for 0 and even (int)0 – Michael Buen Dec 17 '09 at 0:51 2 IMO they should have introduced a keyword none which can be used converted to any enum, and made 0 always an int and not implicitly convertible to an enum. – CodeInChaos Nov 19 '10 at 21:24 5 ConverTo. ToIn32() works because it's result is no compiletime constant.
And only the compiletime constant 0 is convertible to an enum. In earlier versions of . Net even only the literal 0 should have been convertible to enum.
See Eric Lippert's blog: blogs.msdn. Com/b/ericlippert/archive/2006/03/28/563282. Aspx – CodeInChaos Nov 19 '10 at 21:27.
Here's one I only found out about recently... interface IFoo { string Message {get;} } ... IFoo obj = new IFoo("abc"); Console. WriteLine(obj. Message); The above looks crazy at first glance, but is actually legal.No, really (although I've missed out a key part, but it isn't anything hacky like "add a class called IFoo" or "add a using alias to point IFoo at a class").
See if you can figure out why, then: Who says you can’t instantiate an interface?
4 Or indeed read msmvps. Com/blogs/jon_skeet/archive/2009/07/07/… :) – Jon Skeet Aug 15 '09 at 9:52 1 +1 for "using alias" - I never knew you could do that! – David Feb 9 '10 at 4:37.
This is one of the most unusual i've seen so far (aside from the ones here of course! ): public class Turtle where T : Turtle { } It lets you declare it but has no real use, since it will always ask you to wrap whatever class you stuff in the center with another Turtle. Joke I guess it's turtles all the way down... /joke.
Aug 26 '09 at 6:55 23 Indeed. This is the pattern that Java enums use to great effect. I use it in Protocol Buffers too.
– Jon Skeet Aug 26 '09 at 7:09 5 RCIX, oh yes it is. – Joshua Sep 1 '09 at 16:22 8 I have used this pattern quite a lot in fancy generics stuff.It allows things like a correctly typed clone, or creating instances of itself. – Lucero Sep 10 '09 at 19:31 19 This is the 'curiously recurring template pattern' en.wikipedia.
Org/wiki/Curiously_recurring_template_pattern – Porges Nov 5 '09 at 19:30.
Bill discovered that you can hack a boolean so that if A is True and B is True, (A and B) is False. Hacked Booleans.
132 When it's FILE_NOT_FOUND, of course! – Greg Dec 18 '08 at 14:47 12 This is interesting because it means, mathematically speaking, that no statement in C# is provable. Ooops.
– Simon Johnson Feb 1 '09 at 0:56 17 Someday I shall write a program that depends on this behavior, and the demons of darkest hell will prepare a welcome for me. Bwahahahahaha! – Jeffrey L Whitledge Nov 25 '09 at 22:23 18 This example uses bitwise, not logical operators.
How is that surprising? – Josh Lee Nov 26 '09 at 17:02 6 Well, he hacks the layout of the struct, of course you'll get weird results, this is not that surprising or unexpected! – Ion Todirel May 19 '10 at 3:18.
I'm arriving a bit late to the party, but I've got three four five: If you poll InvokeRequired on a control that hasn't been loaded/shown, it will say false - and blow up in your face if you try to change it from another thread (the solution is to reference this. Handle in the creator of the control). Another one which tripped me up is that given an assembly with: enum MyEnum { Red, Blue, } if you calculate MyEnum.Red.ToString() in another assembly, and in between times someone has recompiled your enum to: enum MyEnum { Black, Red, Blue, } at runtime, you will get "Black".
I had a shared assembly with some handy constants in. My predecessor had left a load of ugly-looking get-only properties, I thought I'd get rid of the clutter and just use public const. I was more than a little surprised when VS compiled them to their values, and not references.
If you implement a new method of an interface from another assembly, but you rebuild referencing the old version of that assembly, you get a TypeLoadException (no implementation of 'NewMethod'), even though you have implemented it (see here). Dictionary: "The order in which the items are returned is undefined". This is horrible, because it can bite you sometimes, but work others, and if you've just blindly assumed that Dictionary is going to play nice ("why shouldn't it?
I thought, List does"), you really have to have your nose in it before you finally start to question your assumption.
5 #2 is an interesting example. Enums are compiler mappings to integral values. So even though you didn't explicitly assign them values, the compiler did, resulting in MyEnum.
Red = 0 and MyEnum. Blue = 1. When you added Black, you redefined the value 0 to map from Red to Black.
I suspect that the problem would have manifested in other usages as well, such as Serialization. – LBushkin May 25 '09 at 3:02 2 +1 for Invoke required. At ours' we prefer to explicitly assign values to enums like Red=1,Blue=2 so new one can be inserted before or after it will always result in same value.It is specially necessary if you are saving values to databases.
– TheVillageIdiot Jun 4 '09 at 9:40 52 I disagree that #5 is an "edge case". Dictionary should not have a defined order based on when you insert values. If you want a defined order, use a List, or use a key that can be sorted in a way that's useful to you, or use an entirely different data structure.
– Wedge Jun 26 '09 at 8:30 21 @Wedge, like SortedDictionary perhaps? – Allon Guralnek Aug 22 '09 at 16:54 2 #3 happens because constants are inserted as literals everywhere they're used (in C#, at least). Your predecessor might have already noticed it, which is why they used the get-only property.
However, a readonly variable (as opposed to a const) would work just as well. – Remoun Sep 30 '09 at 9:21.
VB. NET, nullables and the ternary operator: Dim I As Integer? = If(True, Nothing, 5) This took me some time to debug, since I expected I to contain Nothing.
What does I really contain? 0. This is surprising but actually "correct" behavior: Nothing in VB.NET is not exactly the same as null in CLR: Nothing can either mean null or default(T) for a value type T, depending on the context.
In the above case, If infers Integer as the common type of Nothing and 5, so, in this case, Nothing means 0.
I found a second really strange corner case that beats my first one by a long shot. String. Equals Method (String, String, StringComparison) is not actually side effect free.
I was working on a block of code that had this on a line by itself at the top of some function: stringvariable1. Equals(stringvariable2, StringComparison. InvariantCultureIgnoreCase); Removing that line lead to a stack overflow somewhere else in the program.
The code turned out to be installing a handler for what was in essence a BeforeAssemblyLoad event and trying to do if (assemblyfilename. EndsWith("someparticular. Dll", StringComparison.
InvariantCultureIgnoreCase)) { assemblyfilename = "someparticular_modified. Dll"; } By now I shouldn't have to tell you. Using a culture that hasn't been used before in a string comparison causes an assembly load.
InvariantCulture is not an exception to this.
2 Wow. This is a perfect shot into the maintainer's leg. I guess writing a BeforeAssemblyLoad handler can lead to lots of such surprises.
– wigy Feb 1 at 19:05.
Here is an example of how you can create a struct that causes the error message "Attempted to read or write protected memory. This is often an indication that other memory is corrupt". The difference between success and failure is very subtle.
The following unit test demonstrates the problem. See if you can work out what went wrong. Test public void Test() { var bar = new MyClass { Foo = 500 }; bar.
Foo += 500; Assert. That(bar.Foo.Value. Amount, Is.
EqualTo(1000)); } private class MyClass { public MyStruct? Foo { get; set; } } private struct MyStruct { public decimal Amount { get; private set; } public MyStruct(decimal amount) : this() { Amount = amount; } public static MyStruct operator +(MyStruct x, MyStruct y) { return new MyStruct(x. Amount + y.
Amount); } public static MyStruct operator +(MyStruct x, decimal y) { return new MyStruct(x. Amount + y); } public static implicit operator MyStruct(int value) { return new MyStruct(value); } public static implicit operator MyStruct(decimal value) { return new MyStruct(value); } }.
2 Hm I wrote this a few months ago, but I can't remember why exactly it happened. – cbp Aug 6 '09 at 6:50 10 Looks like a compiler bug; the += 500 calls: ldc. I4 500 (pushes 500 as an Int32), then call valuetype Program/MyStruct Program/MyStruct::op_Addition(valuetype Program/MyStruct, valuetype mscorlibSystem.
Decimal) - so it then treats as a decimal (96-bits) without any conversion. If you use += 500M it gets it right. It simply looks like the compiler thinks it can do it one way (presumably due to the implicit int operator) and then decides to do it another way.
– Marc Gravell? Sep 12 '09 at 10:17 1 Sorry for the double post, here's a more qualified explanation. I'll add this, I have been bit by this and this it sucks, even though I understand why it happens.To me this is an unfortunate limitation of the struct/valuetype.
Bytes. Com/topic/net/answers/… – Bennett Dill Oct 9 '09 at 3:59 2 @Ben getting compiler errors or the modification not affecting the original struct is fine.An access violation is quite a different beast. The runtime should never throw it if you're just writing safe pure managed code.
– CodeInChaos Nov 19 '10 at 21:59.
C# supports conversions between arrays and lists as long as the arrays are not multidimensional and there is an inheritance relation between the types and the types are reference types object oArray = new string { "one", "two", "three" }; string sArray = (string)oArray; // Also works for IList (and IEnumerable, ICollection) IList sList = (IList)oArray; IList oList = new string { "one", "two", "three" }; Note that this does not work: object oArray2 = new int { 1, 2, 3 }; // Error: Cannot implicitly convert type 'int' to 'object' int iArray = (int)oArray2; // Error: Cannot convert type 'object' to 'int.
11 The IList example is just a cast, because string already implements ICloneable, IList, ICollection, IEnumerable, IList, ICollection, and IEnumerable. – Lucas Jun 12 '09 at 18:46.
This is the strangest I've encountered by accident: public class DummyObject { public override string ToString() { return null; } } Used as follows: DummyObject obj = new DummyObject(); Console. WriteLine("The text: " + obj.GetType() + " is " + obj); Will throw a NullReferenceException. Turns out the multiple additions are compiled by the C# compiler to a call to String.
Concat(object). Prior to . NET 4, there is a bug in just that overload of Concat where the object is checked for null, but not the result of ToString(): object obj2 = argsi; string text = (obj2!
= null)? Obj2.ToString() : string. Empty; // if obj2 is non-null, but obj2.ToString() returns null, then text==null int length = text.
Length; This is a bug by ECMA-334 §14.7.4: The binary + operator performs string concatenation when one or both operands are of type string. If an operand of string concatenation is null, an empty string is substituted. Otherwise, any non-string operand is converted to its string representation by invoking the virtual ToString method inherited from type object.
If ToString returns null, an empty string is substituted.
3 Hmm, but I can imagine this fault as . ToString really should never return null, but string.Empty. Nevertheless and error in the framework.
– Dykam Mar 1 '10 at 7:44.
Interesting - when I first looked at that I assumed it was something the C# compiler was checking for, but even if you emit the IL directly to remove any chance of interference it still happens, which means it really is the newobj op-code that's doing the checking. Var method = new DynamicMethod("Test", null, null); var il = method.GetILGenerator(); il. Emit(OpCodes.
Ldc_I4_0); il. Emit(OpCodes. Newarr, typeof(char)); il.
Emit(OpCodes. Newobj, typeof(string). GetConstructor(new { typeof(char) })); il.
Emit(OpCodes. Ldc_I4_0); il. Emit(OpCodes.
Newarr, typeof(char)); il. Emit(OpCodes. Newobj, typeof(string).
GetConstructor(new { typeof(char) })); il. Emit(OpCodes. Call, typeof(object).
GetMethod("ReferenceEquals")); il. Emit(OpCodes. Box, typeof(bool)); il.
Emit(OpCodes. Call, typeof(Console). GetMethod("WriteLine", new { typeof(object) })); il.
Emit(OpCodes. Ret); method. Invoke(null, null); It also equates to true if you check against string.
Empty which means this op-code must have special behaviour to intern empty strings.
3 You're not being smart; you're missing the point - I wanted to generate specific IL for this one case. And anyway, given that Reflection. Emit is trivial for this type of scenario, it's probably as quick as writing a program in C# then opening reflector, finding the binary, finding the method, etc... And I don't even have to leave the IDE to do it.
– Greg Beech Jul 14 '09 at 9:35.
PropertyInfo.SetValue() can assign ints to enums, ints to nullable ints, enums to nullable enums, but not ints to nullable enums. EnumProperty. SetValue(obj, 1, null); //works nullableIntProperty.
SetValue(obj, 1, null); //works nullableEnumProperty. SetValue(obj, MyEnum. Foo, null); //works nullableEnumProperty.
SetValue(obj, 1, null); // throws an exception! Full description here.
Public Class Item Public ID As Guid Public Text As String Public Sub New(ByVal id As Guid, ByVal name As String) Me. ID = id Me. Text = name End Sub End Class Public Sub Load(sender As Object, e As EventArgs) Handles Me.
Load Dim box As New ComboBox Me.Controls. Add(box) 'Sorry I forgot this line the first time. ' Dim h As IntPtr = box.
Handle 'Im not sure you need this but you might. ' Try box.Items. Add(New Item(Guid.
Empty, Nothing)) Catch ex As Exception MsgBox(ex.ToString()) End Try End Sub The output is "Attempted to read protected memory. This is an indication that other memory is corrupt.
1 Interesting! Sounds like a compiler bug, though; I've ported to C# and it works fine. That said, there are a lot of issues with exceptions thrown in Load, and it behaves differently with/without a debugger - you can catch with a debugger, but not without (in some cases).
– Marc Gravell? Oct 11 '08 at 21:44 1 This is the same problem cbp above. The valuetype being returned is a copy, therefore any references to any properties stemming from said copy are headed to bit-bucket land... bytes.Com/topic/net/answers/… – Bennett Dill Oct 9 '09 at 4:04 1 Nope.
There are no structs here. I actually debugged it.It adds a NULL to the list item collection of the native combo box causing a delayed crash. – Joshua Oct 9 '09 at 15:24.
I ran into this situation recently writing a two-way dictionary. I wanted to write symmetric Get() methods that would return the opposite of whatever argument was passed. Something like this: class TwoWayRelationship { public T2 Get(T1 key) { /* ... */ } public T1 Get(T2 key) { /* ... */ } } All is well good if you make an instance where T1 and T2 are different types: var r1 = new TwoWayRelationship(); r1.
Get(1); r1. Get("a"); But if T1 and T2 are the same (and probably if one was a subclass of another), it's a compiler error: var r2 = new TwoWayRelationship(); r2. Get(1); // "The call is ambiguous..." Interestingly, all other methods in the second case are still usable; it's only calls to the now-ambiguous method that causes a compiler error.
Interesting case, if a little unlikely and obscure.
1 I don't know, this makes total sense to me. – Scott Whitlock Mar 18 at 17:02.
C# Accessibility Puzzler The following derived class is accessing a private field from its base class, and the compiler silently looks to the other side: public class Derived : Base { public int BrokenAccess() { return base. M_basePrivateField; } } The field is indeed private: private int m_basePrivateField = 0; Care to guess how we can make such code compile? .. ... .. Answer The trick is to declare Derived as an inner class of Base: public class Base { private int m_basePrivateField = 0; public class Derived : Base { public int BrokenAccess() { return base.
M_basePrivateField; } } } Inner classes are given full access to the outer class members.In this case the inner class also happens to derive from the outer class. This allows us to "break" the encapsulation of private members.
Just found a nice little thing today: public class Base { public virtual void Initialize(dynamic stuff) { //... } } public class Derived:Base { public override void Initialize(dynamic stuff) { base. Initialize(stuff); //... } } This throws compile error. The call to method 'Initialize' needs to be dynamically dispatched, but cannot be because it is part of a base access expression.
Consider casting the dynamic arguments or eliminating the base access. If I write base. Initialize(stuff as object); it works perfectly, however this seems to be a "magic word" here, since it does exactly the same, everything is still recieved as dynamic...
In an API we're using, methods that return a domain object might return a special "null object". In the implementation of this, the comparison operator and the Equals() method are overridden to return true if it is compared with null. So a user of this API might have some code like this: return test!
= null? Test : GetDefault(); or perhaps a bit more verbose, like this: if (test == null) return GetDefault(); return test; where GetDefault() is a method returning some default value that we want to use instead of null. The surprise hit me when I was using ReSharper and following it's recommendation to rewrite either of this to the following: return test?GetDefault(); If the test object is a null object returned from the API instead of a proper null, the behavior of the code has now changed, as the null coalescing operator actually checks for null, not running operator= or Equals().
– Ray Booysen Jun 22 '10 at 8:03 1 Yes, the types are nullable - and there is a NullObject in addition. If it is a corner case, I don't know, but at least it is a case where 'if (a! = null) return a; return b;' is not the same as 'return a?
B'. I absolutely agree it is a problem with the framework/API design - overloading == null to return true on an object is certainly not at good idea! – Tor Livar Jun 22 '10 at 14:47.
Consider this weird case: public interface MyInterface { void Method(); } public class Base { public void Method() { } } public class Derived : Base, MyInterface { } If Base and Derived are declared in the same assembly, the compiler will make Base::Method virtual and sealed (in the CIL), even though Base doesn't implement the interface. If Base and Derived are in different assemblies, when compiling the Derived assembly, the compiler won't change the other assembly, so it will introduce a member in Derived that will be an explicit implementation for MyInterface::Method that will just delegate the call to Base::Method. The compiler has to do this in order to support polymorphic dispatch with regards to the interface, i.e.It has to make that method virtual.
The following might be general knowledge I was just simply lacking, but eh. Some time ago, we had a bug case which included virtual properties. Abstracting the context a bit, consider the following code, and apply breakpoint to specified area : class Program { static void Main(string args) { Derived d = new Derived(); d.
Property = "AWESOME"; } } class Base { string _baseProp; public virtual string Property { get { return "BASE_" + _baseProp; } set { _baseProp = value; //do work with the base property which might //not be exposed to derived types //here Console.Out. WriteLine("_baseProp is BASE_" + value.ToString()); } } } class Derived : Base { string _prop; public override string Property { get { return _prop; } set { _prop = value; base. Property = value; } //Property call inside it.
} public string BaseProperty { get { return base. Property; } private set { } } } While in the Derived object context, you can get the same behavior when adding base. Property as a watch, or typing base.
Property into the quickwatch. Took me some time to realize what was going on.In the end I was enlightened by the Quickwatch. When going into the Quickwatch and exploring the Derived object d (or from the object's context, this) and selecting the field base, the edit field on top of the Quickwatch displays the following cast: ((TestProject1.
Base)(d)) Which means that if base is replaced as such, the call would be public string BaseProperty { get { return ((TestProject1. Base)(d)). Property; } private set { } } for the Watches, Quickwatch and the debugging mouse-over tooltips, and it would then make sense for it to display "AWESOME" instead of "BASE_AWESOME" when considering polymorphism.
I'm still unsure why it would transform it into a cast, one hypothesis is that call might not be available from those modules' context, and only callvirt. Anyhow, that obviously doesn't alter anything in terms of functionality, Derived. BaseProperty will still really return "BASE_AWESOME", and thus this was not the root of our bug at work, simply a confusing component.
I did however find it interesting how it could mislead developpers which would be unaware of that fact during their debug sessions, specially if Base is not exposed in your project but rather referenced as a 3rd party DLL, resulting in Devs just saying : "Oi, wait..what? Omg that DLL is like, ..doing something funny.
I'm not sure if you'd say this is a Windows Vista/7 oddity or a . Net oddity but it had me scratching my head for a while. String filename = @"c:\program files\my folder\test.
Txt"; System.IO.File. WriteAllText(filename, ""); bool exists = System.IO.File. Exists(filename); // returns true; string text = System.IO.File.
ReadAllText(filename); // Returns "" In Windows Vista/7 the file will actually be written to C:\Users\\Virtual Store\Program Files\my folder\test.txt.
2 This is indeed a vista (not 7, afaik) security enhancement. But the cool thing is that you can read and open the file with the program files path, while if you look there with explorer there is nothing. This one took me almost a day of work @ a customer before I finally found it out.
– Henri Mar 11 '10 at 22:02.
Run this and you'll get a TypeLoadException: interface I { T M(T p); } abstract class A : I { public abstract T M(T p); } abstract class B : A, I { public override T M(T p) { return p; } public int M(int p) { return p * 2; } } class C : B { } class Program { static void Main(string args) { Console. WriteLine(new C(). M(42)); } } I don't know how it fares in the C# 4.0 compiler though.
EDIT: this is the output from my system: C:\Temp>type Program. Cs using System; using System.Collections. Generic; using System.
Linq; using System. Text; namespace ConsoleApplication1 { interface I { T M(T p); } abstract class A : I { public abstract T M(T p); } abstract class B : A, I { public override T M(T p) { return p; } public int M(int p) { return p * 2; } } class C : B { } class Program { static void Main(string args) { Console. WriteLine(new C().
M(11)); } } } C:\Temp>csc Program. Cs Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1 for Microsoft (R) . NET Framework version 3.5 Copyright (C) Microsoft Corporation.
All rights reserved. C:\Temp>Program Unhandled Exception: System. TypeLoadException: Could not load type 'ConsoleAppli cation1.
C' from assembly 'Program, Version=0.0.0.0, Culture=neutral, PublicKeyTo ken=null'. At ConsoleApplication1.Program. Main(String args) C:\Temp>peverify Program.
Exe Microsoft (R) . NET Framework PE Verifier. Version 3.5.30729.1 Copyright (c) Microsoft Corporation.
All rights reserved. Token 0x02000005 Type load failed.IL: Error: C:\Temp\Program. Exe : ConsoleApplication1.
Program::Mainoffset 0x 00000001 Unable to resolve token.2 Error(s) Verifying Program. Exe C:\Temp>ver Microsoft Windows XP Version 5.1.2600.
1 Works for me in C# 4 – Omer Mor Sep 4 '10 at 6:02.
This one's pretty hard to top. I ran into it while I was trying to build a RealProxy implementation that truly supports Begin/EndInvoke (thanks MS for making this impossible to do without horrible hacks). This example is basically a bug in the CLR, the unmanaged code path for BeginInvoke doesn't validate that the return message from RealProxy.
PrivateInvoke (and my Invoke override) is returning an instance of an IAsyncResult. Once it's returned, the CLR gets incredibly confused and loses any idea of whats going on, as demonstrated by the tests at the bottom. Using System; using System.Collections.
Generic; using System. Linq; using System. Text; using System.Runtime.Remoting.
Proxies; using System. Reflection; using System.Runtime.Remoting. Messaging; namespace BrokenProxy { class NotAnIAsyncResult { public string SomeProperty { get; set; } } class BrokenProxy : RealProxy { private void HackFlags() { var flagsField = typeof(RealProxy).
GetField("_flags", BindingFlags. NonPublic | BindingFlags. Instance); int val = (int)flagsField.
GetValue(this); val |= 1; // 1 = RemotingProxy, check out System.Runtime.Remoting.Proxies. RealProxyFlags flagsField. SetValue(this, val); } public BrokenProxy(Type t) : base(t) { HackFlags(); } public override IMessage Invoke(IMessage msg) { var naiar = new NotAnIAsyncResult(); naiar.
SomeProperty = "o noes"; return new ReturnMessage(naiar, null, 0, null, (IMethodCallMessage)msg); } } interface IRandomInterface { int DoSomething(); } class Program { static void Main(string args) { BrokenProxy bp = new BrokenProxy(typeof(IRandomInterface)); var instance = (IRandomInterface)bp. GetTransparentProxy(); Func doSomethingDelegate = instance. DoSomething; IAsyncResult notAnIAsyncResult = doSomethingDelegate.
BeginInvoke(null, null); var interfaces = notAnIAsyncResult.GetType().GetInterfaces(); Console. WriteLine(!interfaces.Any()? "No interfaces on notAnIAsyncResult" : "Interfaces"); Console.
WriteLine(notAnIAsyncResult is IAsyncResult); // Should be false, is it?! Console. WriteLine(((NotAnIAsyncResult)notAnIAsyncResult).
SomeProperty); Console. WriteLine(((IAsyncResult)notAnIAsyncResult). IsCompleted); // No way this works.
} } } Output: No interfaces on notAnIAsyncResult True o noes Unhandled Exception: System. EntryPointNotFoundException: Entry point was not found.At System.IAsyncResult. Get_IsCompleted() at BrokenProxy.Program.
Main(String args).
There is something really exciting about C#, the way it handles closures. Instead of copying the stack variable values to the closure free variable, it does that preprocessor magic wrapping all occurences of the variable into an object and thus moves it out of stack - straight to the heap! :) I guess, that makes C# even more functionally-complete (or lambda-complete huh)) language than ML itself (which uses stack value copying AFAIK).
F# has that feature too, as C# does. That does bring much delight to me, thank you MS guys! It's not an oddity or corner case though... but something really unexpected from a stack-based VM language :).
1 : 0; The answer provided is pretty good too.
2 Yes, Alex, that would work. The key is in the implicit casting. 1 : 0 alone will implicitly cast to int, not Byte.
– MPelletier May 14 '10 at 3:07.
The following doesn't work: if (something) doit(); else var v = 1 + 2; But this works: if (something) doit(); else { var v = 1 + 2; }.
12 I don't see how it is a corner case... In the first example, there is no way you can use the v variable, since its scope is the else block and you can only have one instruction in it if you don't put braces – Thomas Levesque Aug 10 '10 at 15:44 1 @Anders: Your answers put a lot of focus on the fact that C# differs from C++ like here: stackoverflow. Com/questions/194484/… This thread isn't about the differences between C# and C++. An edge case in C# isn't a difference from C++.
Others have noted you will find answers in the C# spec. – John K Aug 11 '10 at 13:04.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.