我正在尝试使用带有似乎不受支持的约束的泛型,我想知道是否有一个干净的解决方法。
我最初尝试的问题在于接口中不能有静态成员,因此,不能使用接口来约束基于静态成员的泛型声明中的类型。
考虑这个例子:
假设您在通用上下文中工作,但您希望确保 的实例T
能够将自己序列化为流(可以是文件、字节列表),并且T
可以从流中反序列化 的实例。
在编写的情况下,这很容易,因为可以假设您已经有一个实例可以使用(尽管您将无法序列化null
):
public interface IStreamWritable
{
// When called, this IStreamWritable instance
// serializes itself to a stream.
void Write(IStream stream);
}
...
void Write<T>(IStream stream, T t) where T : IStreamWritable
{
t.Write(stream);
}
但是,在阅读的情况下,你会遇到一个问题:
public interface IStreamReadable
{
// When called, returns an instance
// deserialized from the stream.
void Read(IStream stream);
}
...
T Read<T>(IStream stream) where T : IStreamReadable, new()
{
var t = new T();
t.Read(stream);
return t;
}
这似乎可行,但它对如何实例化反序列化的对象做出了假设。也许您想返回一个现有实例而不是创建一个新实例?它还需要new()
约束,这可能是不可取的。
毕竟,当您在特定实例的上下文之外工作时,改为在静态上下文中工作是有意义的。所以你可以试试这个:
public interface IStreamReadable
{
// When called, returns an instance
// deserialized from the stream.
static IStreamReadable Read(IStream stream);
}
...
T Read(IStream stream) where T : IStreamReadable
{
return T.Read(stream);
}
或者,为了避免拳击:
public interface IStreamReadable<T> where T : IStreamReadable<T>
{
// When called, returns an instance
// deserialized from the stream.
static T Read(IStream stream);
}
...
T Read(IStream stream) where T : IStreamReadable<T>
{
return T.Read(stream);
}
不幸的是,两者都无法编译,因为您不能在接口中声明静态成员。但是,如果编译器允许我这样做,那将是理想的解决方案,因为它不对您如何处理实例化做出任何假设,而是将责任交给接口实现者。
我找到了一个适用于结构的不错的解决方案:
public interface IFoo
{
void Foo();
}
...
CallFoo<T>() where T : struct, IFoo
{
return default(T).Foo();
}
实现者IFoo
调用静态方法的地方。当然,由于在这种情况下default(T)
返回,这种方法在引用类型的情况下会失败null
。usingreturn new T().Foo();
也可以,但这需要再次进行约束,并丢弃不必要地创建垃圾new()
的实例。T
我考虑过以某种方式使用反射作为一种解决方法,但我想知道是否有人提出了他们自己的解决方法来解决他们想要分享的这个限制。