﻿<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>Ayende @ Rahien</title><link>http://ayende.com</link><description>Ayende @ Rahien</description><copyright>Copyright (C) Ayende Rahien  2004 - 2021 (c) 2026</copyright><ttl>60</ttl><item><title>Jay R. Wren commented on Tricky Code</title><description>OH!
  
  
I see it now. 
  
  
Yes, that makes MUCH more sense.
  
  
Thanks Jon.
</description><link>http://ayende.com/3077/tricky-code#comment14</link><guid>http://ayende.com/3077/tricky-code#comment14</guid><pubDate>Tue, 08 Jan 2008 22:20:47 GMT</pubDate></item><item><title>Jon Skeet commented on Tricky Code</title><description>List&lt;T&gt; matches T[] because it's a single element, of type List&lt;T&gt;.
  
  
In other words, it's trying to call:
  
  
DoSomething (new List&lt;T&gt;[] { set });
  
  
The important bit which is tricky is that the inferred T of the called DoSomething() is actually List&lt;T&gt; of the *calling* DoSomething.
  
  
Let's change the code to make it easier to understand, by getting rid of the actual recursion. Tiny snippet:
  
  
	static void Main(string[] args)
  
	{
  
		List&lt;int&gt; foo = new List&lt;int&gt;();
  
                DoSomething(foo);
  
	}
  
  
	public static void DoSomething&lt;T&gt;(IList&lt;T&gt; set)
  
	{
  
		// Doesn't matter
  
	}
  
  
	public static void DoSomething&lt;T&gt;(params T[] items)
  
	{
  
               // Doesn't matter
  
	}
  
  
So the "T" inferred for the first DoSomething is int, but the "T" inferred for the second DoSomething is List&lt;int&gt;. The same is true in the original code, but instead of int we're using the type parameter of the calling method, which doesn't change the rules but makes it harder to actually explain.
  
  
Does that help?
  
  
Jon
</description><link>http://ayende.com/3077/tricky-code#comment13</link><guid>http://ayende.com/3077/tricky-code#comment13</guid><pubDate>Tue, 01 Jan 2008 23:36:37 GMT</pubDate></item><item><title>Jay R. Wren commented on Tricky Code</title><description>Jon,
  
  
I see no reason why List&lt;T&gt; should match T[].  They both implement IList&lt;T&gt;, but neither method signature uses IList&lt;T&gt;.
  
  
I think I am definitely missing something.
  
  
I do see #1, that both are applicable due to type inference.
  
  
I don't see #2. What expanded form?  List&lt;T&gt; expands to param T[]? really?  I must say that I am surprised.
  
  
#4 - sure, List&lt;T&gt; to List&lt;T&gt; is better, but where is that match? I only see T[] and IList&lt;T&gt;
  
  
I'll read up on the spec, but I thought I understood.
  
--
  
j
</description><link>http://ayende.com/3077/tricky-code#comment12</link><guid>http://ayende.com/3077/tricky-code#comment12</guid><pubDate>Tue, 01 Jan 2008 21:06:59 GMT</pubDate></item><item><title>Jon Skeet commented on Tricky Code</title><description>Jay, the code is working exactly as it should be. Which bit of the following logic do you disagree with?
  
  
1) Both DoSomething methods are applicable function members due to type inference. The second method is applicable in its expanded form. (Spec 7.4.3.1)
  
  
2) Because the second method is applicable in its expanded form, the parameters are compared for "betterness" in that expanded form (7.4.3.2)
  
  
3) The parameter type sequences aren't identical, so the tie-breaking rules of 7.4.3.2 aren't applied.
  
  
4) The effective conversions are List&lt;T&gt; to IList&lt;T&gt; (the first method), and List&lt;T&gt; to List&lt;T&gt; (the second). The conversion from List&lt;T&gt; to List&lt;T&gt; is better (7.4.3.4).
  
  
All section numbers are from the Unified C# 3 Specification.
  
  
The generics involved make the whole thing a lot more confusing. I think most people would pick the right choice in this situation:
  
  
Invocation: Foo("hello");
  
  
Overloads:
  
void Foo (object o)
  
void Foo (string[] args)
  
  
The rules being applied are the same, it's just that Ayende's situation also has type inference involved, and the types being inferred are different for the two overloads.
  
  
Jon
</description><link>http://ayende.com/3077/tricky-code#comment11</link><guid>http://ayende.com/3077/tricky-code#comment11</guid><pubDate>Tue, 01 Jan 2008 18:47:23 GMT</pubDate></item><item><title>Markus Zywitza commented on Tricky Code</title><description>It's a shame that there is no compiler switch to stop the generic type inference in C#...
  
  
see also http://mortslikeus.blogspot.com/2007/11/wtf-overloading-and-generics.html
</description><link>http://ayende.com/3077/tricky-code#comment10</link><guid>http://ayende.com/3077/tricky-code#comment10</guid><pubDate>Tue, 01 Jan 2008 16:18:42 GMT</pubDate></item><item><title>Omer Mor commented on Tricky Code</title><description>Wicked!!!
  
  
I wonder if there's any use for the type:
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;System.Collections.Generic.List&lt;
  
System.Collections.Generic.List&lt;string&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;	
  
  
:-)
  
  
Omer.
</description><link>http://ayende.com/3077/tricky-code#comment9</link><guid>http://ayende.com/3077/tricky-code#comment9</guid><pubDate>Mon, 31 Dec 2007 19:56:10 GMT</pubDate></item><item><title>Jay R. Wren commented on Tricky Code</title><description>I consider this a bug in the compiler.
  
  
I disagree that the code works exactly as it should be, unless someone can point to the C# spec and show me where this is defined.
</description><link>http://ayende.com/3077/tricky-code#comment8</link><guid>http://ayende.com/3077/tricky-code#comment8</guid><pubDate>Mon, 31 Dec 2007 16:31:23 GMT</pubDate></item><item><title>Jon Skeet commented on Tricky Code</title><description>Agreed. That way you also avoid callers from other methods creating nested lists (or sets) when they didn't want to.
  
  
Anything that avoids having to wade through the overload resolution part of the spec, including the now-nearly-impenetrable type inference rules, is good :)
  
</description><link>http://ayende.com/3077/tricky-code#comment7</link><guid>http://ayende.com/3077/tricky-code#comment7</guid><pubDate>Mon, 31 Dec 2007 12:55:45 GMT</pubDate></item><item><title>Ayende Rahien commented on Tricky Code</title><description>Well, if you add make the variable an IList&lt;T&gt; instead of a List&lt;T&gt;, it will work.
  
You can cast it as well, to force it.
  
The best would be to skip overloading, I feel
</description><link>http://ayende.com/3077/tricky-code#comment6</link><guid>http://ayende.com/3077/tricky-code#comment6</guid><pubDate>Mon, 31 Dec 2007 12:48:45 GMT</pubDate></item><item><title>Jon Skeet commented on Tricky Code</title><description>Ick. Yes. I see.
  
  
I'd love to say that I'd have spotted the problem with this new code, but I'd be lying through my teeth.
  
  
So, what was your fix? Making the call DoSomething&lt;T&gt;(set); or something else?
  
</description><link>http://ayende.com/3077/tricky-code#comment5</link><guid>http://ayende.com/3077/tricky-code#comment5</guid><pubDate>Mon, 31 Dec 2007 12:44:23 GMT</pubDate></item><item><title>Ayende Rahien commented on Tricky Code</title><description>Skeet,
  
I changed that to IList&lt;T&gt; and List&lt;T&gt;, now try it.
</description><link>http://ayende.com/3077/tricky-code#comment4</link><guid>http://ayende.com/3077/tricky-code#comment4</guid><pubDate>Mon, 31 Dec 2007 12:32:47 GMT</pubDate></item><item><title>Chris Bilson commented on Tricky Code</title><description>Sorry: typo above: never happens to _m_e...
</description><link>http://ayende.com/3077/tricky-code#comment3</link><guid>http://ayende.com/3077/tricky-code#comment3</guid><pubDate>Mon, 31 Dec 2007 12:13:07 GMT</pubDate></item><item><title>Chris Bilson commented on Tricky Code</title><description>params bugs are one of my favorite low hanging fruit things to look for when I hear someone has a stack overflow (never happens to be...no..nu-uh...never.) 
  
  
It's too bad the compiler can't catch these, though.
</description><link>http://ayende.com/3077/tricky-code#comment2</link><guid>http://ayende.com/3077/tricky-code#comment2</guid><pubDate>Mon, 31 Dec 2007 12:11:53 GMT</pubDate></item><item><title>Jon Skeet commented on Tricky Code</title><description>Where do ISet&lt;T&gt; and HashedSet&lt;T&gt; come from? NHibernate?
  
  
Using HashSet&lt;T&gt; from .NET 3.5, this prints "2" as I'd expect.
  
  
I'm now intrigued as to what your version does...
  
  
Jon
  
  
</description><link>http://ayende.com/3077/tricky-code#comment1</link><guid>http://ayende.com/3077/tricky-code#comment1</guid><pubDate>Mon, 31 Dec 2007 11:59:08 GMT</pubDate></item></channel></rss>