8

我有一个相当简单的问题,但 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;
    }
}

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

4

8 回答 8

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);
于 2013-11-11T15:19:16.747 回答
3

问题是您尝试将其FromBytes用作扩展方法,而事实并非如此。根据我收集到的信息,您正在尝试调用适当FromBytes的任何<T>内容,如您已经为T.

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

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

使用反射在具有签名的对象实例上调用泛型方法: SomeObject.SomeGenericInstanceMethod<T>(T argument)

如何使用反射调用泛型方法?

在 .NET 中使用反射调用泛型方法

如何使用给定的 Type 对象调用泛型方法?

于 2013-11-11T13:27:28.007 回答
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....
    }
}

正如其他答案所指出的那样,您还可以在此处隐藏该反射代码。不幸的是,似乎没有一种非常干净的方法可以做到这一点,但是将杂乱的东西隐藏在实用程序方法中可能是最好的选择。

于 2013-11-11T13:22:25.250 回答
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;
    }
}

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

于 2013-11-11T13:43:22.237 回答
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;
}
于 2013-11-11T13:51:53.643 回答
1

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

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

界面:

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;
    }
}
于 2013-11-11T15:52:03.073 回答
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;
    }
}
于 2013-11-12T07:41:40.113 回答
0

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

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

  • 通过反射
  • 来自来电者

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

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

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

于 2014-03-28T08:21:46.337 回答