Evil Code #007: Overloads

Time to do a bit of Evil Code again. Let’s look into following set of classes first.

public class BaseParameter { }
public class DerievedParameter :BaseParameter { }

public class BaseClass
{
   public void Foo(DerievedParameter param) => Console.WriteLine($"Inside {nameof(BaseClass)}");
}

public class DerievedClass : BaseClass
{
   public void Foo(BaseParameter param) => Console.WriteLine($"Inside {nameof(DerievedClass)}");
}

The client code looks like following.

var instance = new DerievedClass();
instance.Foo(new DerievedParameter());
instance.Foo(new BaseParameter());

What could be output of the above ? There might be tendency that the one might be inclined to think about an output that resembles following.

Inside BaseClass
Inside DerievedClass

But interestingly that is not quite right. The overload rules ensures that when there is a probable method in the most derieved compile type, the derieved method is chosen, even if there is a better choice in base class. The output of the code would be as following.

Inside DerievedClass
Inside DerievedClass

This is one of the reasons why overloading of base class methods are not recommended.

Let’s take a step further and introduce IEnumerable as method parameters. That’s when things get even more interesting.


public class BaseClass
{
  public void Foo(DerievedParameter param) => Console.WriteLine($"Inside {nameof(BaseClass)}");
  public void Bar(IEnumerable<DerievedParameter> paramList) => Console.WriteLine($"Inside {nameof(BaseClass)}");
}

public class DerievedClass : BaseClass
{
  public void Foo(BaseParameter param) => Console.WriteLine($"Inside {nameof(DerievedClass)}");
  public void Bar(IEnumerable<BaseParameter> paramList) => Console.WriteLine($"Inside {nameof(DerievedClass)}");
}
 instance.Bar(new List<DerievedParameter> { new DerievedParameter(), new DerievedParameter() });
instance.Bar(new List<BaseParameter> { new BaseParameter(), new BaseParameter() });

What could be the output of above ? As per our earlier knowledge, the output would be

Inside DerievedClass
Inside DerievedClass

Well, the actual answer is slightly more complicated. If you are running the code in a framework above 4.0, then you are bang on target. But, if the target framework is lesser, you are in for a surprise.

Inside BaseClass
Inside DerievedClass

The reason for the above scenario is that post 4.0, C# generic interfaces support covariance and contravariance, which wasn’t the case for frameworks prior to that. This results in above scenario.

Happy Coding

Advertisements

Evil Code #006 : Build Configurations

This one is courtesy Wouter de Kort, from his famous book. Okay, so what’s the evil code here. Consider the following code and predict the output in Debug as well as Release modes.

static int count = 10;
static void Main(string[] args)
{
  Timer timer = new Timer(CallBackMethod, null, 0, 2000);
  Console.ReadLine();
}

private static void CallBackMethod(object state)
{
  if (--count == 0)
  Environment.Exit(0);

  Console.Write(count);
  GC.Collect();
}

Output of the code under different configurations

// Debug Mode
987654321
// Release Mode
9

Isn’t that weird that the output has varied in Debug and Release modes ? Well, the key player in charge of this different output is the Garbage Collector and code optimization. When in the Release mode, the compiler optimizes the code realizing that timer object is not used again. This results in the CallBackMethod being executed only once.

Evil Code #005 : Readonly Value Types

Time to review another of Evil Code Series and this time, we are dealing with read-only value types. Review the below code and predict the output.

 

struct DummyStruct
{
    public int MyValue {get; set;}
    public int Increment() => ++MyValue;
    public DummyStruct(int myValue)=> MyValue = myValue;
}
public class DemoClass
{
    private readonly DummyStruct _readOnlyStruct;
    public DemoClass() => _readOnlyStruct = new DummyStruct(2);
    public void IncrementAndDisplayValue() => Console.WriteLine($"({_readOnlyStruct.Increment()},{_readOnlyStruct.MyValue})");
}
var myClass = new DemoClass();
myClass.IncrementAndDisplayValue();

 

Strangely enough, the output is {3,2}. Yes, it means the Increment Method return a value of 3 for MyValue, but a follow-up statement to display MyValue explicitly, ends up with a value 2. Isn’t that bizarre ? Well not quite if one think a bit deeper.

Being a value type, whenever we are accessing it, we only get a copy of the value. The original _readOnlyStruct.MyValue is immutable and doesn’t change. But, the copy isn’t quite immutable and gets altered, which is returned by the Increment method. And when you are calling the _readOnlyStruct.MyValue property in the later part of program, another copy of the immutable value type is created which has the original value. This is the reason we are seeing the output. A more detailed explanation has been provided by Eric Lippert in his blog .
Happy Coding !!

Evil Code #004 : Conditional Attribute

Continuing with our series of subtle changes that make differences, let’s look into Conditional Attributes over Directives.

static void Main(string[] args)
{
Method01();
Method02();

Queue queue = new Queue();
queue.Enqueue("One");
queue.Enqueue("Two");
queue.Enqueue("Three");

Method03(queue.Dequeue());
Console.WriteLine(queue.Count);
Console.WriteLine("All Done");
Console.ReadLine();
}

// Approach One
private static void Method01()
{
#if DEBUG
Console.WriteLine("Hello Method01");
#endif
}
[Conditional("DEBUG")]
private static void Method02()
{
Console.WriteLine("Hello Method02");
}

[Conditional("DEBUG")]
private static void Method03(string Value)
{
Console.WriteLine(Value);
}

What would be the output of above code under
a) Debug Mode
b) Release Mode

The Debug Mode should be easy and as expected

Hello Method01
Hello Method02
One
2
All Done

What about release mode ?
3
All Done

You might have a tendency to say “2”, considering you might expect the dequeue statement to be executed. However, that is not the case. The dequeue is not called either because the result is not used as the method is not called.This leads to unexpected errors .
This is one the reasons why it is recommended not to decorate a method that accepts parameter with conditional Attributes.

Evil Code #003 : Casting

Welcome to first post on ‘Back to basics’. Yeah, as you guessed right, this series attempt to check minor differences in our understanding of basic concepts.

Predict the output of following.


public class FirstType
{

}
public class SecondType
{
private FirstType _mType;

public static implicit operator FirstType(SecondType t)
{
return t._mType;
}
}

static void Main(string[] args)
{
object objectInstance = new SecondType();

FirstType attempt1 = objectInstance as FirstType;
if (attempt1 != null)
{
Console.WriteLine("'As' Conversion Succeeded");
}
else
{
Console.WriteLine("'As' Conversion Failed");
}

try
{
FirstType attempt2 = (FirstType)objectInstance;
Console.WriteLine("Cast Succeeded");
}
catch (Exception)
{
Console.WriteLine("Cast Failed");
}

}

Ideally, One would expect the first attempt to fail, but would feel the second would succeed considering we have implemented implicit conversion. But unfortunately, that’s not the case. The output would be

‘As’ Conversion Failed.
Cast Failed.

The reason for this is that, the compiler generates code based on the compile time type of the objectInstance, which in this case is ‘object’. It then checks if there is any conversion declared to convert object to FirstType, which doesn’t exist. The compiler doesn’t check the type of the objectInstance during runtime, which results in the above output.

The reason for this is that, the compiler generates code based on the compile time type of the objectInstance, which in this case is ‘object’. It then checks if there is any conversion declared to convert object to FirstType, which doesn’t exist. The compiler doesn’t check the type of the objectInstance during runtime, which results in the above output.

Updated

The behavior is even more tricky when we change the type of objectInstance from ‘object’ to SecondType. We would expect it to work smoothly now, however, you end up with a compiler exception this time .

“Cannot convert type ‘Cast.SecondType’ to ‘Cast.FirstType’ via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion”

This is what makes ‘as’ operators great as it provides early detection of errors. The ‘as’ operator, notices that there is a implicit conversion declared between FirstType and SecondType. However, it also recognizes that two types aren’t related by inheritance. This is the reason ‘as’ statements throws a compile-time error at this stage.

 

Evil Code 0002 Optional Parameter Vs Method Overloading

Again one of those things which looks so simple, but could drive one mad during debugging.

Much before Optional Parameters came in, C# developers used to depend on Method Overloading for mimicking Optional Parameters. Consider a scenario where both exists, what would be the output ?

Image

The output would be :

Image

The reason behind this is the overload resolution method adopted when we have a overloaded method and a method with optional parameter. Here is what MSDN says.

If two candidates are judged to be equally good, preference goes to a candidate that does not have optional parameters for which arguments were omitted in the call. This is a consequence of a general preference in overload resolution for candidates that have fewer parameters.

Evil Code 0001 : Lambda and Ref/out

What would be output of following ?

        static void Main(string [] args)
        {
            List< int> numlist = new List< int>() {1,2, 3, 4, 5, 6, 7 };
            CalculateAndPrint( ref numlist);
        }
 
        public static void CalculateAndPrint( ref List< int> Num)
        {
            var n = Num.Where(d => d > Num[2]);
 
            foreach ( var item in n)
            {
                Console.WriteLine(item);
            }
        }

The most obvious answer is 4,5,6,7. However that’s not. This code refuses to compile because Lamba expressions  cannot accept variables that are declared in an outer scope.