C#:支持重写的类型特定/静态方法

C#:支持重写的类型特定/静态方法,c#,static,types,overriding,C#,Static,Types,Overriding,在C#中,不可能重写静态方法。尽管如此,我还是有一些特定于类型的方法(而不是通常的实例方法),我想让用户能够重写这些方法,此外,还可以从我自己的代码中调用这些(重写的)方法,而不知道用户所做的“重写”。如果方法是静态的,我必须事先知道用户类的类型名,这不是我想要的 如何实现特定于类型的方法,如读取、写入和获取长度并允许重写? 背景 有一个抽象的行类,其派生类型/类表示一种行类型,其实例表示一个非常简单的表中的实际行及其字段/数据。每种类型的行都有固定的长度,表只是文件中从头到尾的行。Row类需要

在C#中,不可能重写静态方法。尽管如此,我还是有一些特定于类型的方法(而不是通常的实例方法),我想让用户能够重写这些方法,此外,还可以从我自己的代码中调用这些(重写的)方法,而不知道用户所做的“重写”。如果方法是静态的,我必须事先知道用户类的类型名,这不是我想要的

如何实现特定于类型的方法,如
读取
写入
获取长度
并允许重写?

背景 有一个抽象的
类,其派生类型/类表示一种行类型,其实例表示一个非常简单的表中的实际行及其字段/数据。每种类型的行都有固定的长度,表只是文件中从头到尾的行。
Row
类需要三种方法:
Read
Write
方法,它们在给定偏移量的流上执行明显的功能,以及
GetLength
方法,返回行类型的固定长度

现在,用户可以扩展我的
类,并为其特定行类型提供
读取
写入
获取长度
的实现,以及在其特定行实例中使用的字段和属性。例如,
SomeUserRow
类可能有一个32位整数字段和一个单字节字段、5字节的固定长度以及相应的读写方法实现

方法 阅读
一个明显的工厂方法,与类型相关,因此我将在类本身中定义它。我会使它成为静态的,但是它不能被重写。然后我可以使它成为
protectedabstract
,并创建一个静态泛型
Read
方法来调用它,如中所建议的。但是我还需要能够在不知道用户实现的类的类型的情况下从代码中调用此方法。我不能只调用
Row.Read()
,因为我还不知道用户的类型

写 一种可能的实例方法,因为大多数人希望将现有的
写入流。但是有了
Read
static,将
Write
作为一个实例方法似乎很奇怪

GetLength 不是工厂方法,但仍与类型相关。同样,我会将其设置为静态,但这会阻止重写。我可以选择将其作为一个实例方法,它可以被重写,但在面向对象的环境中这样做感觉是错误的:创建一个实例只是为了得到一个值,该值不依赖于实例(
int length=new T().GetLength()
),而是依赖于它的类型

我也一直在考虑将这三个方法从类中移到一个单独的类中,但这仍然不能解决重写问题。或者有一个类来保存指向正确方法的委托列表或字典。它不允许真正的重写(替换委托数组中的方法指针不是我认为真正的重写),它将类型特定的方法从我认为不好的类型中移出,从开发人员的角度来看:当您想要更改类型时,有两个或多个位置需要更改。
通过反射,可以对一个类型调用正确的静态方法,但是当我读取很多行时,我发现它太慢了。

我认为您应该使用
读取方法制作
IRowReader
接口。然后可以有多个实现,返回
(如果需要,还可以有一个子类,然后可以覆盖
GetLength

换句话说,您需要一个工厂类型,其中包含您需要重写的方法。正如您所说的,您不能重写C#中的静态方法


当然,问题是,如何获得正确的IRowReader实例?这取决于应用程序,但您可以让客户机在启动时或应用程序中的其他定义良好的点对其进行初始化;或者你可以使用它来注入和配置。

我也遇到了这个问题,令人沮丧的是没有“干净”的解决方案。这是我使用的一个变通方法,也许你会喜欢

假设您有一个以
为基类的层次结构

public class Row { }
public class DerivedRow : Row { }
创建另一个与此层次结构并行的层次结构,但它基于一个名为
Factory
的抽象基类

public abstract class Factory {
    public abstract Row Read(Stream stream);
    public abstract int GetLength();
}
public class RowFactory : Factory {
    public override Row Read(Stream stream) { /* read a base row */ }
    public override int GetLength() { return /* length of base row */ }
}
public class DerivedRowFactory : Factory {
    public override Row Read(Stream stream) { /* read a derived row */ }
    public override int GetLength() { return /* length of derived row */ }
}
现在您可以编写将工厂作为类型参数的方法,例如

public IEnumerable<Row> ReadRows<TRowFactory>(Stream stream)
    where TRowFactory : Factory, new()
{
    var factory = new TRowFactory();
    var numRows = stream.Length / factory.GetLength();
    for (long i = 0; i < numRows; i++)
        yield return factory.Read(stream);
}

// Example call...
var rowsInFile = ReadRows<DerivedRowFactory>(File.Open(...));
public IEnumerable<Row> ReadRows<TRow>(Stream stream)
    where TRow : Row
{
    var numRows = stream.Length / RowUtils.GetLength<TRow>();
    for (long i = 0; i < numRows; i++)
        yield return RowUtils.Read<TRow>(stream);
}

// Example call...
var rowsInFile = ReadRows<DerivedRow>(File.Open(...));
public IEnumerable可读行(流)
其中TRowFactory:Factory,new()
{
var factory=new TRowFactory();
var numRows=stream.Length/factory.GetLength();
对于(长i=0;i
我想抽象基类(RowBase)和工厂类可以解决这个问题。通过使用配置文件或其他方法,可以确定您感兴趣的派生类的类型。基本上,您在这里使用的是策略模式。

一种可能的策略是使用静态方法,您将通过反射发现该方法。幸运的是,您只需要编写一次反射代码

假设您有一个包含静态
Read
GetLength
方法的层次结构:

public class Row {
    public static Row Read(Stream stream) { /* read a base row */ }
    public static int GetLength() { return /* length of base row */ }
}
public class DerivedRow : Row {
    public static DerivedRow Read(Stream stream) { /* read a derived row */ }
    public static int GetLength() { return /* length of derived row */ }
}
现在,您可以编写一个实用程序函数来读取任何类型的行:

public static class RowUtils
{
    public static Row Read<T>(Stream stream) where T : Row
    {
        var method = typeof(T).GetMethod("Read",
            BindingFlags.Static | BindingFlags.Public);
        if (method == null || !typeof(Row).IsAssignableFrom(method.ReturnType))
            throw new InvalidOperationException(string.Format(
                "Static Read method on type “{0}” does not exist or " +
                "has the wrong return type.", typeof(T).FullName));

        return (Row) method.Invoke(null, new object[] { stream });
    }
}

“一个明显的工厂方法,与类型相关,因此我将在类本身中定义它。”这就是你犯错误的地方。如果它是一个工厂方法,那么它应该属于一个工厂类,它应该是一个可以被子类工厂覆盖的实例方法