是否有通用类使用静态方法的解决方法?

时间:2013-11-11 13:19:42

标签: c# generics reflection

我有一个相当简单的问题,但在C#中似乎没有解决方案。

我有大约100个Foo个类,每个类都实现static FromBytes()方法。还有一些泛型类应该将这些方法用于它自己的FromBytes()。但是泛型类不能使用static FromBytes()方法,因为T.FromBytes(...)是非法的。

我是否遗漏了某些内容或者无法实现此功能?

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;
    }
}

我认为,在所有答案和评论以不同的方式对不同的观点做出贡献后,选择答案作为公认的答案是不公平的。如果有人用不同的方法对不同的方法进行了很好的概述,那就太好了。在它帮助未来的开发人员做到最好之后,这应该被接受。

8 个答案:

答案 0 :(得分:5)

最好的选择是简单地接受特定的FromBytes函数作为通用FromBytes函数的委托。这避免了使用反射时出现的性能成本和编译时可验证性。

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;
}

请注意,如果您创建方法而不是它所使用的类,那么您可以让编译器推断泛型参数。它可以这样调用:

var list = SomeClass.FromBytes(bytes, ref index, Foo1.FromBytes);

答案 1 :(得分:3)

问题在于,您尝试使用FromBytes作为扩展方法。根据我收集的内容,您尝试为FromBytes所针对的任何<T>调用适当的T,正如您已为{{1}}创建的任何课程中所定义的那样。

您需要做的是通过反射调用方法。

尝试查看其中一些线程,以获取有关如何完成此操作的帮助。

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?

答案 2 :(得分:2)

您是否可以在实用程序类中实现静态FromBytes方法?然后做这样的事情?

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

如果每个方法FromBytes有很多不同,那么实用程序方法可能有点难看,但它不会太糟糕,无论如何它都是过去常常存在于每个类中的所有代码。

这样的事情:

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....
    }
}

你也可以在这里隐藏反射代码,如其他答案所指出的那样。不幸的是,似乎没有非常干净的方法来做到这一点,但是在实用方法中隐藏杂乱的东西可能是最好的选择。

答案 3 :(得分:1)

您可以尝试这样的事情:

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;
    }
}

您构建一个查找以查找要调用以构建实例的方法。

答案 4 :(得分:1)

您可以使用反射来查找该类型的方法,然后为其构建委托。类似的东西:

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;
}

答案 5 :(得分:1)

不确定这是否适合您,但这是一个选项:

  1. 定义一个定义IHaveFromBytesMethod<T>实例方法的接口FromBytes(...)
  2. 使所有Foo#类型实现此接口。 (如果您不希望此实例方法立即可见,则可以显式实现该接口。)
  3. 实例方法的所有实现只是将调用转发给该类型的静态方法。
  4. 限制T类中的类型参数ListOfFoo<T>以实现此接口并提供无参数构造函数。
  5. 当您需要静态方法时,使用无参数构造函数构造一个类型为T的新虚拟对象,然后在该虚拟实例上调用实例方法。
  6. 接口:

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

    其中一个Foo#类:

    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);
        }
    }
    

    修改后的ListOfFoo<T>类:

    // 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;
        }
    }
    

答案 6 :(得分:0)

我要感谢所有人,你给了我一些思考的见解。我已经考虑了每种方法,并考虑了它的优点和缺点,并编写了以下代码。你有什么想法?我认为这是对可用性,可读性和性能的良好折衷。它只缺少编译器检查。

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;
    }
}

答案 7 :(得分:0)

这将是所有未来观众的摘要。

由于您不能只调用该方法,主要问题是获取委托。有两种方法:

  • 通过反思
  • 来自来电者的

您可能认为反思很慢,但性能不是问题。如果委托存储在静态字段中,则每个类只需要执行一次,因为泛型类型不共享静态成员。

编译时间可验证性是一个问题。如果您非常关心编译时可验证性,那么您应该从调用者那里传递委托。如果你更关心一个干净的方法调用,你必须牺牲编译时的可验证性。

PS:有些人建议使用字典或开关/案例或if / else存储代理。这是你不应该做的事情。这没有优于将委托存储在泛型类的静态字段中(泛型类型不共享静态成员)。