0

I want to create an open delegate to the method ToString(string, IFormatprovider) of a struct (Int32, DateTime, whatever):

public delegate string MyCoverter(ref DateTime from, string format, IFormatProvider provider);
...
var method = typeof(DateTime).GetMethod("ToString", new[] { typeof(string), typeof(IFormatProvider)}); // Works!
var d= Delegate.CreateDelegate(typeof(MyCoverter), null, method); // Exception!

It keeps throwing the ArgumentException with message "Error binding to target method.".

I've read almost all stackoverflow articles on this subject, I have experimented with and without ref, I have added and removed the null when creating the delegate. Nothing seems to help.

Has anyone any clue?

* EDIT *

When I create my own struct with the same method, the code above (with DateTime replaced my MyStruct) works perfectly fine.

public struct MyStruct
{
    public string ToString(string format, IFormatProvider provider)
    {
        return null;
    }
}

What makes an Int32 or DateTime so different?

* EDIT *

As requested I added a complete "working" program. What I forgot to mention: I am working on .NET framework 3.5. Furthermore, as I stated before, this is all working on MyStruct. Except, when I implement the interface IFormattable, it also does not work anymore.

using System;

namespace OpenDelegates
{
    public delegate string MyCoverter<T>(ref T from, string format, IFormatProvider provider)
        where T : struct;

    class Program
    {
        static void Main(string[] args)
        {
            var method = typeof(MyStruct).GetMethod("ToString", new[] { typeof(string), typeof(IFormatProvider) });
            var d = Delegate.CreateDelegate(typeof(MyCoverter<MyStruct>), null, method);

            method = typeof(DateTime).GetMethod("ToString", new[] { typeof(string), typeof(IFormatProvider) });
            d = Delegate.CreateDelegate(typeof(MyCoverter<DateTime>), null, method);
        }
    }

    public struct MyStruct //: IFormattable
    {
        public string ToString(string format, IFormatProvider provider)
        {
            return null;
        }
    }
}

* EDIT *

It all works perfectly on .NET Framework 4.x, but that is NOT a solution for me.

Martin Mulder
  • 12,642
  • 3
  • 25
  • 54
  • 1
    There definitly is no overload for ToString with a ref-parameter. In particular you can´t convert your delegate to any of the `ToString`-overloads. – MakePeaceGreatAgain Apr 18 '17 at 08:39
  • 1
    I ran your code and didn't get an error...Please can you create an [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve)... – RB. Apr 18 '17 at 08:40
  • 1
    @HimBromBeere: It is an OPEN delegate, meaning the first parameter is the instance (`this`) the method is working on. According to some articles, this parameter should be refered to when it is a struct. See: http://stackoverflow.com/questions/1212346/uncurrying-an-instance-method-in-net/1212396#1212396 – Martin Mulder Apr 18 '17 at 08:44
  • @RB: This code is literaly copy-pasted out of my code (from the Main-method). But I will make an example inside a Main-method. – Martin Mulder Apr 18 '17 at 08:45
  • @RB.: Code sample added. Be aware I am using .NET Framework 3.5. (And in here lies the problem, I suspect). – Martin Mulder Apr 18 '17 at 08:57
  • Reproduced issue on VS2010 and .NET 3.5 – MakePeaceGreatAgain Apr 18 '17 at 08:59

1 Answers1

3

I don't know the answer for that specific problem but maybe you can build your own delegate:

public static Func<object, string, IFormatProvider, string> CreateConverter<T>()
    where T : struct // not really needed
{
    var method = typeof(T).GetMethod("ToString", new[] { typeof(string), typeof(IFormatProvider) });
    if (method == null)
    {
        throw new InvalidOperationException(string.Format("The type {0} does not contain a suitable ToString-method.", typeof(T).FullName));
    }

    var instanceParameter = Expression.Parameter(typeof(object), "instance");
    var formatParameter = Expression.Parameter(typeof(string), "format");
    var providerParameter = Expression.Parameter(typeof(IFormatProvider), "provider");

    var convertedInstance = Expression.Convert(instanceParameter, typeof(T));
    var methodCall = Expression.Call(convertedInstance, method, formatParameter, providerParameter);
    var lambda = Expression.Lambda<Func<object, string, IFormatProvider, string>>(methodCall, instanceParameter, formatParameter, providerParameter);
    return lambda.Compile();
}

Can be used like:

var d = CreateConverter<MyStruct>();
Console.WriteLine(d(new MyStruct(), "", CultureInfo.CurrentCulture));

d = CreateConverter<DateTime>();
Console.WriteLine(d(DateTime.Now, "yyyydd", CultureInfo.CurrentCulture));

EDIT to make sure that the input is of the correct type

public static Func<T, string, IFormatProvider, string> CreateConverter<T>()
{
    var method = typeof(T).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string), typeof(IFormatProvider) }, null);
    if (method == null)
    {
        throw new InvalidOperationException(string.Format("The type {0} does not contain a suitable ToString-method.", typeof(T).FullName));
    }

    var instanceParameter = Expression.Parameter(typeof(T), "instance");
    var formatParameter = Expression.Parameter(typeof(string), "format");
    var providerParameter = Expression.Parameter(typeof(IFormatProvider), "provider");

    var methodCall = Expression.Call(instanceParameter, method, formatParameter, providerParameter);
    var lambda = Expression.Lambda<Func<T, string, IFormatProvider, string>>(methodCall, instanceParameter, formatParameter, providerParameter);
    return lambda.Compile();
}
Sebastian Schumann
  • 3,204
  • 19
  • 37
  • Look good. If you could make sure the input parameter of the delegate (now object) is always of the correct type (DateTime, etc.) than it would be great! :) – Martin Mulder Apr 18 '17 at 09:39
  • @MartinMulder Of course you can. Just replace `object` by `T`. The conversion to `T` is no longer needed. See my edit. – Sebastian Schumann Apr 18 '17 at 09:43