views:

1012

answers:

4

See the four lines in the Go() method below:

delegate void Action<T>(T arg);
delegate void Action();

void DoSomething<T>(Action<T> action)
{
    //...
}

void DoSomething(Action action)
{
    //...
}

void MyAction<T>(T arg)
{
    //...
}

void MyAction()
{
    //...
}

void Go<T>()
{
    DoSomething<T>(MyAction<T>); // throws compiler error - why?
    DoSomething(new Action<T>(MyAction<T>)); // no problems here
    DoSomething(MyAction); // what's the difference between this...
    DoSomething(new Action(MyAction)); // ... and this?
}

Note that the compiler error generated by the first call is: The type arguments for method 'Action(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

+6  A: 

There's no difference between MyAction and new Action(MyAction) (when they're both valid) other than the former won't work in C# 1. This is an implicit method group conversion. There are times that this isn't applicable, most notable when the compiler can't work out what kind of delegate you want, e.g.

Delegate foo = new Action(MyAction); // Fine
Delegate bar = MyAction; // Nope, can't tell target type

This comes into play in your question because both of the methods involved are overloaded. This leads to headaches, basically.

As for the generics side - it's interesting. Method groups don't get much love from C# 3 type inference - I'm not sure whether that's going to be improved in C# 4 or not. If you call a generic method and specify the type argument, type inference works fairly well - but if you try to do it the other way round, it fails:

using System;

class Test
{
    static void Main()
    {
        // Valid - it infers Foo<int>
        DoSomething<int>(Foo);
        // Valid - both are specified
        DoSomething<int>(Foo<int>);
        // Invalid - type inference fails
        DoSomething(Foo<int>);
        // Invalid - mismatched types, basically
        DoSomething<int>(Foo<string>);
    }

    static void Foo<T>(T input)
    {
    }

    static void DoSomething<T>(Action<T> action)
    {
        Console.WriteLine(typeof(T));
    }
}

Type inference in C# 3 is very complicated, and works well in most cases (in particular it's great for LINQ) but fails in a few others. In an ideal world, it would become easier to understand and more powerful in future versions... we'll see!

Jon Skeet
Cheers! I accepted the other answer but yours is still helpful too. +1
Nathan Ridley
+2  A: 

The non-generic implicit delegate creation is just syntactic sugar, so the compiler generates exactly the same code for

DoSomething(MyAction);

and

DoSomething(new Action(MyAction));

as it can infer the type of the delegate directly from the method arguments & context.

With the generic delegate, you have to specify the delegate type due to covariance and contravariance (see http://msdn.microsoft.com/en-us/library/ms173174(VS.80).aspx for details) - the T in Action can be a supertype to the T in the method, and it will still be accepted as a delegate method. So, you need to specify the T in the delegate explicitly as the compiler can't figure it out itself.

thecoop
Cheers, now I understand. Link to covariance vs contravariance article for those who don't understand: http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
Nathan Ridley
I'd say it's more to do with the limitations of type inference than co/contravariance, personally. If you read the spec section on type inference (I can't remember it offhand now) you'll see that method groups basically don't get much of a chance.
Jon Skeet
A: 

Ok, follow up. Here is the full code as requested. Note the use of lambda expressions below; earlier I used explicit delegate methods which is where a problem occurred.

public static class IDataReaderExtensions
{
 public delegate T ObjectCreationHandler<T>();

 public static IDataReader Each(this IDataReader dr, Action<IDataReader> action)
 {
  while (dr.Read())
   action(dr);
  return dr;
 }

 public static IDataReader If(this IDataReader dr, bool condition, Action<IDataReader> action)
 {
  if (condition)
   action(dr);
  return dr;
 }

 public static T Return<T>(this IDataReader dataReader, T returnObject)
 {
  dataReader.Close();
  dataReader.Dispose();
  return returnObject;
 }

 public static T Return<T>(this IDataReader dataReader)
 {
  return dataReader.Return(() => Activator.CreateInstance<T>());
 }

 public static T Return<T>(this IDataReader dataReader, ObjectCreationHandler<T> createObject)
 {
  T result = dataReader.MapTo(createObject);
  dataReader.Close();
  dataReader.Dispose();
  return result;
 }

 public static IDataReader MapTo<T>(this IDataReader dataReader, out T result)
 {
  result = dataReader.MapTo(() => Activator.CreateInstance<T>());
  return dataReader;
 }

 public static IDataReader MapTo<T>(this IDataReader dataReader, out T result, ObjectCreationHandler<T> createObject)
 {
  result = dataReader.MapTo(createObject);
  return dataReader;
 }

 public static T MapTo<T>(this IDataReader dataReader)
 {
  return dataReader.MapTo(() => Activator.CreateInstance<T>());
 }

 public static T MapTo<T>(this IDataReader dataReader, ObjectCreationHandler<T> createObject)
 {
  // return null/default if the datareader is invalid
  if (dataReader.FieldCount == 0)
   return default(T);

  // if there is no current record (as signified by a lack of fields) and we fail to read a record, return null/default
  if (dataReader.FieldCount == -1 && !dataReader.Read())
   return default(T);

  if (typeof(T).IsPrimitive || typeof(T).Equals(typeof(string)))
   return (T)Convert.ChangeType(dataReader[0], typeof(T));

  T newObject = createObject();

  foreach (var property in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty))
  {
   // to do...
  }
  return newObject;
 }
}
Nathan Ridley
A: 

hi ...A simple search will give lots of informations in the net.

http://www.google.co.in/search?hl=en&amp;source=hp&amp;q=difference+between+implicit+and+explicit+type&amp;aq=f&amp;aqi=&amp;oq=&amp;cad=h

Thanks, MONU

Monu