Ayende @ Rahien

Refunds available at head office

C# Coding Challenge: What will this code do?

What is the output of this code?

 IDictionary<object,string> instanceToKey = new Dictionary<object, string>();

 IDictionary<string, int> keyToCost = new Dictionary<string, int>();

 var inst1 = new object();
 var inst2 = new object();

 instanceToKey[inst1] = "one";
 keyToCost["one"] = 1;

 instanceToKey[inst2] = "two";
 keyToCost["two"] = 2;

 string value = null;
 foreach (var key
     in (from inst in new[]{inst1, inst2, new object()}
         where instanceToKey.TryGetValue(inst, out value) 
         select value))
 {
     int cost;
     if(keyToCost.TryGetValue(key, out cost))
         Console.WriteLine("Key: {0}, Cost: {1}", key, cost);
 }
Tags:

Posted By: Ayende Rahien

Published at

Originally posted at

Comments

Steve Py
05/21/2010 06:51 AM by
Steve Py

Well it's bloody convoluded, but I couldn't see anything wrong with it. The 3rd instance will be ignored because of the TryGetValue.

Key: One, Cost: 1

Key: Two, Cost: 2

I thought you might have a gotcha in there so I also tried it in .Net 3.5 and 4.0 but got the same result... Is something missing?

Ken Egozi
05/21/2010 06:54 AM by
Ken Egozi

Am I missing something? it should be the trivial "one"/1 and "two"/2 pairs

dan
05/21/2010 07:28 AM by
dan

haven't touched .net for a while, but inst1 and inst2 are both new object() - will they have identical hash codes for the dictionary?

if so, the result will be:

Key: two, Cost: 2

Key: two, Cost: 2

Key: two, Cost: 2

Glenn
05/21/2010 08:07 AM by
Glenn

+1 to Steve's comment. TryGetValue returns false for the 3rd object and it's never selected.

kukabuka
05/21/2010 08:17 AM by
kukabuka

I was going to agree with dan, but if you run the code it is just

Key: One, Cost: 1

Key: Two, Cost: 2

inst1.Equals(inst2) = false. .GetHashCode() will reveal different hashes too. I guess that makes the difference to the Dictionary type.

rvin100
05/21/2010 09:24 AM by
rvin100

I'm agree with Steve too. It's a strange way to process, but you just got :

Key: One, Cost: 1

Key: Two, Cost: 2

The third instance is just ignored...

Ayende Rahien
05/21/2010 09:29 AM by
Ayende Rahien

This is actually what I call a reverse gotcha.

It works as you expect it to, but it doesn't look like it will work.

Stefan Wenig
05/21/2010 10:00 AM by
Stefan Wenig

Sweet! Code that relies on delayed execution, string interning and the semantics of mutating captured variables. That's much better than last time's prefix decrement operator!

Next time, can we have some lock-free multi-threaded code along with the question "what will this code print on an Itanium system?"

Please! ;-) (you might need to specify the chipset though, but then at least participants can't just run the code to solve the quiz!)

Omer Mor
05/21/2010 10:29 AM by
Omer Mor

Why doesn't it look like it will work?

Looking at the code, it seemed just fine to me.

Felipe Fujiy
05/21/2010 10:33 AM by
Felipe Fujiy

GetHashCode for classes by default return a unique token by instannce

Andrey Titov
05/21/2010 10:44 AM by
Andrey Titov

I'd rewrite this in something like:

foreach (var keyCost in

new[] { inst1, inst2, new object() }

.ApplyMap(instanceToKey, (inst, key) => key)

.ApplyMap(keyToCost, (key, cost) => new { key, cost }))

{

Console.WriteLine("Key: {0}, Cost: {1}", keyCost.key, keyCost.cost);

}

public static IEnumerable <tresult ApplyMap <tresult,> (

this IEnumerable

<tsource source,

Func

<tsource,> keySelector,

IDictionary

<tkey,> map,

Func

<tsource,> resultSelector

)

{

foreach (var item in source)

{

    TKey key = keySelector(item);

    TValue value;

    if (map.TryGetValue(key, out value))

    {

        yield return resultSelector(item, key, value);

    }

}

}

TryGetValue() newer fit well in LINQ :-(

Andrey Titov
05/21/2010 10:46 AM by
Andrey Titov

I forgot second overload:

public static IEnumerable <tresult ApplyMap <tresult,> (

this IEnumerable

<tsource source,

IDictionary

<tsource,> map,

Func

<tsource,> resultSelector

)

{

return ApplyMap(source, key => key, map, (item, key/*=item*/, value) => resultSelector(item, value));

}

Steve Py
05/21/2010 11:02 AM by
Steve Py

"This is actually what I call a reverse gotcha.

It works as you expect it to, but it doesn't look like it will work. "

rofl... Intentionally making something more obscure than it needs to be? That whole debate over Raven DB licensing must have put you in that sort of mood. ;)

Daniel
05/21/2010 11:19 AM by
Daniel

To everyone who thinks the is working correctly: change the order in the array so that the 3rd instance is in the middle:

new[]{inst1, new object(), inst2}

Then try to explain out why changing the position of an element that seems to be ignored changes the output of the program.

configurator
05/21/2010 12:04 PM by
configurator

Daniel: I don't see why it would. And it doesn't.

meisinger
05/21/2010 04:13 PM by
meisinger

Thats funny... have what is called "reverse reverse gotchas" all over the place

Code that looks like it shouldn't work and doesn't work

(oh I love the double negative)

I haven't checked but I am pretty sure that I can also produce just the simple "gotcha"

Code that looks like it should work but doesn't work

fschwiet
05/21/2010 06:43 PM by
fschwiet

well the loop input

from inst in new[]{inst1, inst2, new object()} where instanceToKey.TryGetValue(inst, out value) select value

is equivalent to:

new[] {inst1, inst2, new object()}.Where(inst => instanceToKey.TryGetValue(inst, out value)).Select(

                inst => value)

I assumed this was equivalent to

new[] {inst1, inst2, new object()}.Where(inst => instanceToKey.TryGetValue(inst, out value)).ToArray().Select(

                inst => value)

But its not. So I was all sorts of wrong.

Diego Mijelshon
05/22/2010 05:40 PM by
Diego Mijelshon

"It works as you expect it to, but it doesn't look like it will work." -> which is why it's important to write the tests first. If it passes, you don't need to code anything else.

Comments have been closed on this topic.