8

I have a rather simple problem, but there seems to be no solution within C#.

I have around 100 Foo classes where each implement a static FromBytes() method. There are also some generic classes which shall use these methods for its own FromBytes(). BUT the generic classes cannot use the static FromBytes() methods, because T.FromBytes(...) is illegal.

Do I miss something or is there no way to implement this functionality?

public class Foo1
{
    public static Foo1 FromBytes(byte[] bytes, ref int index)
    {
        // build Foo1 instance
        return new Foo1()
        {
            Property1 = bytes[index++],
            Property2 = bytes[index++],
            // [...]
            Property10 = bytes[index++]
        };
    }

    public int Property1 { get; set; }
    public int Property2 { get; set; }
    // [...]
    public int Property10 { get; set; }
}

//public class Foo2 { ... }
// [...]
//public class Foo100 { ... }

// Generic class which needs the static method of T to work
public class ListOfFoo<T> : System.Collections.Generic.List<T>
{
    public static ListOfFoo<T> FromBytes(byte[] bytes, ref int index)
    {
        var count = bytes[index++];
        var listOfFoo = new ListOfFoo<T>();
        for (var i = 0; i < count; i++)
        {
            listOfFoo.Add(T.FromBytes(bytes, ref index)); // T.FromBytes(...) is illegal
        }

        return listOfFoo;
    }
}

I think it would be unfair to choose an answer as accepted answer, after all answers and comments have contributed in different ways with their different views. It would be nice if someone writes a good overview about the different approaches with their pros and cons. That should be accepted after it helps future developers best.

Karsten Gutjahr
  • 316
  • 2
  • 12
  • 1
    Try extension method. – Hossain Muctadir Nov 11 '13 at 13:22
  • 1
    Hi @Muctadir, unfortunately I have the same problems in the generic extension method as in the generic class. – Karsten Gutjahr Nov 11 '13 at 13:28
  • How do you call the static `ListOfFoo.FromBytes(...)` method? You will have to specify the type argument anyway, won't you? For example: `ListOfFoo.FromBytes(...)`. Then you can pass a delegate as an additional parameter (like [Servy proposed](http://stackoverflow.com/a/19909773/273527)). Or do you call it via reflection? – gehho Nov 11 '13 at 17:03

8 Answers8

5

The best option would be to simply accept the specific FromBytes function as a delegate to your generic FromBytes function. This avoids both the performance cost and lack of compile time verifiability that comes along with using reflection.

public delegate T FromBytesFunc<T>(byte[] bytes, ref int index);
public static List<T> FromBytes<T>(byte[] bytes, ref int index,
    FromBytesFunc<T> function)
{
    var count = bytes[index++];
    var listOfFoo = new List<T>();
    for (var i = 0; i < count; i++)
    {
        listOfFoo.Add(function(bytes, ref index));
    }

    return listOfFoo;
}

Note that if you make the method, rather than the class it's in, generic, you can get the compiler to infer the generic argument. It could be called like so:

var list = SomeClass.FromBytes(bytes, ref index, Foo1.FromBytes);
Servy
  • 202,030
  • 26
  • 332
  • 449
3

The problem is that you're trying to use FromBytes as an extension method when it's not. From what I gather, you're trying to call the appropriate FromBytes for whatever <T> is, as defined in whatever class you've already created for T.

What you need to do is call the method via reflection.

Try looking at some of these threads for help on how to accomplish this.

Use Reflection to call generic method on object instance with signature: SomeObject.SomeGenericInstanceMethod<T>(T argument)

How do I use reflection to call a generic method?

Calling generic method using reflection in .NET

How to call generic method with a given Type object?

Community
  • 1
  • 1
Smeegs
  • 9,151
  • 5
  • 42
  • 78
  • If I'd use reflection to find out the type I can call ´Foo42.FromBytes()´. But I cannot add the resulting instance to the list: ´listOf.Add(Foo42.FromBytes(Bytes, ref index));´ gives me "Argument type 'Foo42' is not assignable to Parameter type T.". – Karsten Gutjahr Nov 11 '13 at 13:37
  • Hmmm, have you tried casting the result? `(T)Foo42.FromBytes(Bytes, ref index)`? – Smeegs Nov 11 '13 at 13:39
  • Hmmm, sorry I couldn't be more help. But I'm not sure. Perhaps try reposting this question but with reflection in the title. You might attract the reflection gurus. – Smeegs Nov 11 '13 at 14:07
  • 1
    Try casting to `object` and then to `T`: `(T)((object)Foo42.FromBytes(Bytes, ref index))` – gehho Nov 11 '13 at 15:03
2

Could you just implement the static FromBytes method generically in a utility class? Then do something like this?

listOf.Add(Utility.FromBytes<T>(bytes, ref index));

That utility method might be a bit ugly if each methods FromBytes is a lot different, but it wouldn't be too bad, it's all code that used to live in each class anyway.

Something like this:

public static class Utility
{
    public static T FromBytes<T>(byte[] bytes, ref int index)
    {
          if (typeof(T) is Foo1)
          {
               return Foo1.GetBytes(bytes, ref index);
          }
          //etc....
    }
}

You could also hide that reflection code here as pointed out by other answers. Unfortunately there doesn't seem to be a very clean way to do this, but hiding the messy stuff in a utility method may be the best option.

Kevin DiTraglia
  • 25,746
  • 19
  • 92
  • 138
1

You can try something like this:

sealed class RetType
{
    public object Value
    {
        get;
        private set;
    }

    public int Index
    {
        get;
        private set;
    }

    public RetType(object value, int index)
    {
        Value = value;
        Index = index;
    }
}

public class ListOfFoo<T> : System.Collections.Generic.List<T>
{
    static readonly Dictionary<Type, Func<byte[], int, RetType>> dic = new Dictionary<Type, Func<byte[], int, RetType>>
    {
        {
            typeof(Foo1),
            new Func<byte[], int, RetType>((bytes, index) =>
            {
                var value = Foo1.FromBytes(bytes, ref index);

                return new RetType(value, index);
            })
        }
        // add here others Foo
    };

    public static ListOfFoo<T> FromBytes(byte[] bytes, ref int index)
    {
        var count = bytes[index++];
        var listOf = new ListOfFoo<T>();
        for (var i = 0; i < count; i++)
        {
            var o = dic[typeof(T)](bytes, index);

            listOf.Add((T)o.Value);

            index = o.Index;
        }

        return listOf;
    }
}

You build a lookup to finde the method you want to call to build the instance.

Alessandro D'Andria
  • 8,663
  • 2
  • 36
  • 32
  • So you're saying the dictionary has to be filled manually for each `Foo`? That doesn't sound like a very good solution. – svick Nov 11 '13 at 13:45
  • Obviously, and I agree that it is not a good solution but it is a solution. The other obvious option is reflection as proposed above. – Alessandro D'Andria Nov 11 '13 at 13:49
  • I am also interested in not-so-good solutions, but for the compiler the ´T´ of ´RetType´ is not the same ´T´ as in ´ListOfFoo´, just share the same name. Therefor the compiler cannot cast the one to the other... :-( – Karsten Gutjahr Nov 11 '13 at 14:12
  • @KarstenGutjahr ok i reworked the code and tested it, give it a look. – Alessandro D'Andria Nov 11 '13 at 14:44
  • Nice, that seems reasonable. I am just afraid that this code can be condensed down to `(T)((object)Foo42.FromBytes(Bytes, ref index))` which was commented here 20 minutes after your comment. – Karsten Gutjahr Nov 11 '13 at 15:19
  • I'm sure the filling of the Dictionary can be automated: `foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(t => t.Namespace == "Foos")) { Dict.Add(type, (FromBytesFunc)Delegate.CreateDelegate(typeof(FromBytesFunc), type.GetMethod("FromBytes"))); }` with `private delegate T2 FromBytesFunc(byte[] bytes, ref int index);` But then you have no compiler check. – Karsten Gutjahr Nov 12 '13 at 08:05
1

You can use reflection to find the method on that type and then build a delegate for it. Something like:

delegate T FromBytesFunc<T>(byte[] bytes, ref int index);

public static ListOfFoo<T> FromBytes(byte[] bytes, ref int index)
{
    FromBytesFunc<T> fromBytes =
        (FromBytesFunc<T>)Delegate.CreateDelegate(
            typeof(FromBytesFunc<T>), typeof(T).GetMethod("FromBytes")

    var count = bytes[index++];
    var listOf = new ListOfFoo<T>();
    for (var i = 0; i < count; i++)
    {
        listOf.Add(fromBytes(bytes, ref index));
    }

    return listOf;
}
svick
  • 236,525
  • 50
  • 385
  • 514
1

Not sure if this works for you, but it is an option:

  1. Define an interface IHaveFromBytesMethod<T> which defines a FromBytes(...) instance method.
  2. Make all Foo# types implement this interface. (If you do not want this instance method to be visible immediately, you could implement the interface explicitly.)
  3. All implementations of the instance method simply forward the call to the static method of that type.
  4. Restrict type parameter T in your ListOfFoo<T> class to implement this interface and to provide a parameterless constructor.
  5. When you need the static method, construct a new dummy object of type T using the parameterless constructor and then call the instance method on that dummy instance.

Interface:

public interface IHaveFromBytesMethod<T>
{
    T FromBytes(byte[] bytes, ref int index);
}

One of the Foo# classes:

public class Foo1 : IHaveFromBytesMethod<Foo1>
{
    public Foo1()
    {
        // ...
    }

    public static Foo1 FromBytes(byte[] bytes, ref int index)
    {
        // ...
    }

    public Foo1 FromBytes(byte[] bytes, ref int index)
    {
        // within the instance method simply call the static method
        return Foo1.FromBytes(bytes, ref index);
    }
}

The modified ListOfFoo<T> class:

// requires T to implement the interface and provide a parameterless ctor!
public class ListOfFoo<T> : System.Collections.Generic.List<T>
    where T : IHaveFromBytesMethod<T>, new()
{
    public static ListOfFoo<T> FromBytes(byte[] bytes, ref int index)
    {
        // create dummy instance for accessing static method via instance method
        T dummy = new T();
        var count = bytes[index++];
        var listOfFoo = new ListOfFoo<T>();
        for (var i = 0; i < count; i++)
        {
            // instead of calling the static method,
            // call the instance method on the dummy instance
            listOfFoo.Add(dummy.FromBytes(bytes, ref index));
        }

        return listOfFoo;
    }
}
gehho
  • 9,049
  • 3
  • 45
  • 59
  • That's really ugly. Logically, it doesn't make sense to construct an instance of `Foo` to construct another instance of it. – svick Nov 11 '13 at 16:29
  • It's true. But aren't all solutions on this page ugly in some way? I just wanted to propose an alternative which works without reflection. – gehho Nov 11 '13 at 16:59
0

I'd like to thank you all guys, you gave me some insights to think about. I've considered each approach and thought about its pros and con and wrote the following code. What do you think about? I think it is a good compromise for usability, readability and performance. It only lacks the compiler check.

public class ListOfFoo<T> : System.Collections.Generic.List<T>
{
    private static readonly FromBytesFunc<T> BytesFromFunc = 
        (FromBytesFunc<T>)System.Delegate.CreateDelegate(
            typeof(FromBytesFunc<T>),
            typeof(T).GetMethod("FromBytes"));

    private delegate T2 FromBytesFunc<out T2>(byte[] bytes, ref int index);

    public static ListOfFoo<T> FromBytes(byte[] bytes, ref int index)
    {
        var count = bytes[index++];
        var listOfFoo = new ListOfFoo<T>();
        for (var i = 0; i < count; i++)
        {
            listOfFoo.Add(BytesFromFunc(bytes, ref index));
        }

        return listOfFoo;
    }
}
Karsten Gutjahr
  • 316
  • 2
  • 12
0

This shall be the summary for all future viewers.

Since you cannot just call the method, the main problem is to get the delegate. There are two approaches:

  • via reflection
  • from the caller

You might think reflection is slow, but performance is not an issue. If the delegate is stored in a static field, it only needs to be done once per class, because generic types don't share static members.

Compile time verifiability is an issue. If you care much about compile time verifiability you should go with passing the delegate from the caller. If you care more about a clean method call you have to sacrifice the compile time verifiability.

PS: Some people have suggested to have a dictionary or a switch/case or if/else where the delegates are stored. This is something you shouldn't do. This has no advantage over storing the delegate in a static field of the generic class (generic types don't share static members).

Karsten Gutjahr
  • 316
  • 2
  • 12