在不更改类的情况下更改一个方法的行为的最简单方法

本文关键字:方法 一个 最简单 情况下 | 更新日期: 2023-09-27 18:08:31

一点背景:我使用IQToolkit编写一个使用System.Data.Odbc而不是System.Data.SqlClient的自定义数据提供程序。

我遇到了默认参数器类的问题,在这里找到。下面的代码片段是我需要修改的,总共只有一个方法。

    int iParam = 0;
    protected override Expression VisitConstant(ConstantExpression c)
    {
        if (c.Value != null && !IsNumeric(c.Value.GetType())) {
            NamedValueExpression nv;
            TypeAndValue tv = new TypeAndValue(c.Type, c.Value);
            if (!this.map.TryGetValue(tv, out nv)) { // re-use same name-value if same type & value
                string name = "p" + (iParam++);
                nv = new NamedValueExpression(name, this.language.TypeSystem.GetColumnType(c.Type), c);
                this.map.Add(tv, nv);
            }
            return nv;
        }
        return c;
    }

我想了好几种方法来完成这个任务,但是我都不喜欢。

  1. 我有源代码,所以我可以改变源代码本身并重新编译它。但是,这将为其他SQL方言删除一些有用的代码,我不想仅仅为了ODBC而破坏这一切。

  2. 我可以写我自己的Parameterizer类,并使用它而不是默认的。如果我这样做,我将复制250行代码,只是为了改变三到四行。

  3. 我可以尝试重写类,只改变我需要的方法。我试过了,但是我在Parameterizer类的保护水平上遇到了一些问题。我最喜欢这个,但我不确定我做得很正确,或者如果我甚至可以用给定的类设计。

道歉,如果问题似乎模糊,但有没有任何简单的方法让我改变该方法的行为,而不必改变基类或不复制其所有的代码?

EDIT:问题#3:不能访问IsNumeric(私有Parameterizer),不能访问Parameterizer.map,不能访问TypeAndValue结构体,不能访问Parameterizer.language

在不更改类的情况下更改一个方法的行为的最简单方法

这就是我的想法(基于我们的评论)。

public class MyVisitor : DbExpressionVisitor
{
    // cast it to the visitor interface/base - that way you'd have access to all methods,
    // as Parameterizer has to have them publically exposed anyway, for this to work
    readonly DbExpressionVisitor _defaultVisitor;
    public MyVisitor()
    {
        _defaultVisitor = new Parameterizer();
    }
    public Expression VisitProjection(ProjectionExpression proj)
    {
        return _defaultVisitor.Visit(proj);
    }
    // ...same for all others, just...
    // ... 
    // implement your own for one 'route'
    protected Expression VisitConstant(ConstantExpression c)
    {
        if (c.Value != null && !IsNumeric(c.Value.GetType())) {
            NamedValueExpression nv;
            TypeAndValue tv = new TypeAndValue(c.Type, c.Value);
            if (!this.map.TryGetValue(tv, out nv)) { // re-use same name-value if same type & value
                string name = "p" + (iParam++);
                nv = new NamedValueExpression(name, this.language.TypeSystem.GetColumnType(c.Type), c);
                this.map.Add(tv, nv);
            }
            return nv;
        }
        return c;
    }
}

访客,访客模式

"从本质上讲,访问器允许一个人给a添加新的虚拟功能不修改类本身的类族"

。这意味着在大多数情况下,这本身就足够了,不需要求助于继承和重写(这有点多余,有点简化)。您可以使用这个事实来覆盖原始访问者的某些行为(因为您已经有了适当的机制),从而避免继承和可能出现的问题。

唯一可能的问题是- Parameterizer没有默认的行为-我认为这是不可能的可能无论如何都需要。

最终重写参数器更容易。不确定这是否广泛相关,但我必须更改代码,以防有人感兴趣:

这个方法的问题是,它试图重用具有重复类型和值的参数。但是,由于我使用的是未命名的ODBC样式参数,因此查询必须具有与查询指定的相同数量的参数,即使它们完全相同。

protected override Expression VisitConstant(ConstantExpression c)
{
    if (c.Value != null && !IsNumeric(c.Value.GetType())) {
        NamedValueExpression nv;
        TypeAndValue tv = new TypeAndValue(c.Type, c.Value, iParam);
        string name = "p" + (iParam++);
        nv = new NamedValueExpression(name, this.language.TypeSystem.GetColumnType(c.Type), c);
        this.map.Add(tv, nv);
        return nv;
    }
    return c;
}

我还必须修改TypeAndValue构造函数,使参数的哈希值唯一,即使它们的类型和值相同。我通过添加参数number来实现这一点。

public TypeAndValue(Type type, object value, int pCount)
{
    this.type = type;
    this.value = value;
    this.hash = type.GetHashCode() + (value != null ? value.GetHashCode() : 0) + pCount;
}