我有一个相当简单的问题,但在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;
}
}
我认为,在所有答案和评论以不同的方式对不同的观点做出贡献后,选择答案作为公认的答案是不公平的。如果有人用不同的方法对不同的方法进行了很好的概述,那就太好了。在它帮助未来的开发人员做到最好之后,这应该被接受。
答案 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}}创建的任何课程中所定义的那样。
您需要做的是通过反射调用方法。
尝试查看其中一些线程,以获取有关如何完成此操作的帮助。
How do I use reflection to call a generic method?
答案 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)
不确定这是否适合您,但这是一个选项:
IHaveFromBytesMethod<T>
实例方法的接口FromBytes(...)
。Foo#
类型实现此接口。 (如果您不希望此实例方法立即可见,则可以显式实现该接口。) T
类中的类型参数ListOfFoo<T>
以实现此接口并提供无参数构造函数。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;
}
}
答案 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存储代理。这是你不应该做的事情。这没有优于将委托存储在泛型类的静态字段中(泛型类型不共享静态成员)。