在C#/VB.NET中是否有伪继承的方法?

5

假设我想要继承于 System.Data.SqlClient.SqlTransaction ,但该类是密封的(sealed)。假设我只想在SqlTransaction上面加一个包装器,并且总是使用MyTransaction而不是SqlTransaction。是否有一种方法可以使用隐式/扩大运算符将MyTransaction潜在地转换为SqlTransaction


5
为什么您要这样做呢?翻译的内容是“假设我想继承自System.Data.SqlClient.SqlTransaction”。 - Darin Dimitrov
4
达林是正确的,你正在要求钻孔而不是告诉我们为什么你首先需要在那个钢梁上打孔。事实上,该类型已被封装,这是一个巨大的警示灯,表示我不希望你从中继承。为什么你要违背类设计者的意愿呢?他们心里最在乎的是你的利益。解释一下你真正想要达成什么目标,因为无法从一个封装的类进行继承。 - Eric Lippert
1
使用反射无法取消类的密封吗? - Chris Marisic
1
我想要封装一个事务,因为我想要追踪谁创建了它,在提交时产生事件,并且还有一堆其他我想要跟踪的自定义内容,比如事务开放了多长时间等等。这是 SqlTransaction 所没有的。 - Denis
这个追踪需要在哪里进行?客户端?服务器端?你将如何研究这些事务?对我来说,一些自定义连接字符串属性(机器名称、应用程序名称等)可以在连接上设置并通过SQL可见。嗯...这是一个Web应用程序,不是一个厚客户端吗? - Tevo D
显示剩余2条评论
8个回答

6
您可以创建一个具有内部交易变量并公开方法和属性的类。就像这样:
public class MyTransaction
{
    System.Data.SqlTransaction myTx = someConnection.CreateTransaction();

    public void CommitTransaction() :  {
        myTx.CommitTransaction()
    }
}

你也可以让它继承DbTransaction,然后重写抽象和虚拟过程以使用内部的myTx变量,但这样做没有明显的实际原因就会变得有些复杂了...


1
当然可以!但如果这个人想要封装一个事务,可能是有原因的! - Alejandro B.
仅仅因为他想做,即使这是可能的,也不意味着他应该这样做。 - Security Hound
@Ramhound 我同意,但这仍然是一个学习其他更适合的情况下解决方案的好方法。 - Alejandro B.

3
如果您只是想给一个类添加额外的方法,您可以使用扩展方法。这不会让您访问任何内部状态,也不允许您覆盖行为,但它将允许您添加有限的功能。我不知道有任何方法可以继承自密封类。
正如其他人所提到的,您可以创建一个真正的包装对象,但您将无法在原始对象的位置上使用它进行多态操作。

这是在.NET 2.0中,所以没有扩展方法...但我还需要跟踪一些东西,因此扩展方法也无法使用。 - Denis
虽然由于 .net 2 的原因这并不重要,但扩展方法可以使用以第一个参数(即被操作的对象)为键的字典来跟踪独立的对象数据。这可以提供一组“分区”的数据,类似于内部状态但存储在对象外部。通过反射可以访问内部成员,因此根据代码的“丑陋”程度,您可以做一些有趣的事情。 - Bradley Uffner
希望你不介意,我在你的问题中添加了一个 .net2.0 标签,以便其他人知道框架版本是有限制的。 - Bradley Uffner

3

如果你真的需要隐式转换(尽管我不建议这样做,因为在我看来这是一个可怕的想法和可怕的设计),你可以像这样操作:

    class MyTransaction
    {
        private readonly SqlTransaction _transaction;

        public MyTransaction(SqlConnection conn)
        {
            _transaction = conn.BeginTransaction();
        }

        public SqlTransaction Transaction
        {
            get
            {
                return _transaction;
            }
        }

        public static implicit operator SqlTransaction(MyTransaction t)
        {
            return t.Transaction;
        }
    }

不幸的是,这是VB.NET,他们的扩展运算符似乎不能像C#的“隐式”那样工作,这是我刚刚发现的,但我也考虑过这条路……尽管像你说的那样,我也不喜欢它…… - Denis
@Denis 你可以尝试使用C#编写该类,然后在你的VB应用程序中使用DLL。 - Chris Marisic
1
使用C#编写代码,使用Reflector反汇编为VB语法。 - TomTom

2

好的,排除继承并专注于您真正想解决的任务(基于评论线程)。

过去我曾经成功地通过帮助库运行所有调用并在那里实现逻辑。在过去,我使用了SqlHelper,它发布在Microsoft的数据应用程序块中。这是一个源模块,您可以根据自己的需求进行适应。您可以添加任何所需的日志记录或其他逻辑。

它还使代码非常易读。您可以做像以下这样的事情:

SqlHelper.ExecuteDataset()用于返回数据集的查询,

SqlHelper.ExecuteScalar()用于返回单个值的查询,

SqlHelper.ExecuteNonQuery()用于没有返回值的命令(如INSERT)。

等等。


1

不,你不能让你的自定义类继承SqlTransaction或者伪造它。

然而,如果你所做的事情允许你使用DbTransaction,你可以从DbTransaction继承你的自定义事务类,在其中包装一个SqlTransaction并添加任何其他所需功能。


我以为这也可以工作,但是你需要像SqlClient.SQLCommand.Transaction这样的东西,它需要SqlTransaction。 - Denis
是的,你会陷入一整个包装对象的混乱中...你可以使用来自System.Data命名空间的所有自定义包装器来包装SQL版本....这完全取决于你的要求以及为什么首先要处理这些对象。很可能有其他(更好的)方法来实现目标。 - Tevo D

1

你还有另一种选择,可以使用Reflection.Emit()将你选择的接口添加到SqlTransaction中,然后在你的新MyTransaction类中使用同样的接口,这样你就可以调用接口而不是类。

请注意,这仅适用于你创建的库或使用Reflection特定修改加载的类型。


我从未尝试过Reflection.Emit()。我将四处寻找,但您是否有任何关于如何使用它的好例子/教程? - Denis

1
你可以创建扩展方法。
public static class SqlTransactionExtensions
{
    public static void DoSomething(this SqlTransaction transaction, int myParameter)
    {
        // do something here
    }
}

类必须是静态的。在第一个参数前面放置魔术字“this”,该参数必须是您正在扩展的类的类型。您也可以扩展接口。如果您想使用此扩展方法,则必须有一个具有此扩展类命名空间的using namespace,如果它没有在您正在使用的相同命名空间中定义。
然后,您可以像调用 SqlTransaction 的常规方法一样调用扩展方法:
SqlTransaction t = new SqlTransaction();
t.DoSomething(5);

这是一个好主意,但有两个问题:(1)我正在使用.NET 2.0,(2)如果我不使用.NET 2.0,我怎么能使用扩展方法在创建SqlTransaction时创建对象? (假设我想要跟踪谁创建了这个sqlTransaction或开始时间是什么) - Denis
我会为交易创建一个静态的帮助类。就像扩展类一样,但没有“this”关键字。不再使用“t.DoSomething(5);”调用方法,而是必须使用“TransactionsHelper.DoSomething(t,5);”进行调用。扩展方法只是语法糖,不能做任何普通方法无法完成的事情。 - Olivier Jacot-Descombes

0
你可以定义自己的 MyTransaction 类,作为 SqlTransaction 的包装器。在 MyTransaction 内部保留一个 SqlTransaction 实例的私有字段。你的包装器将无法与 SqlTransaction 进行赋值兼容,但如果你实现和 SqlTransaction 相同的接口,你就可以实现相似的功能。

我以为这也可以工作,但是你会遇到像SqlClient.SQLCommand.Transaction这样的东西,它需要SqlTransaction而不是IDbTransaction,这真的很糟糕,所以我需要一些方法来模拟这个,这就开始了我的探索... - Denis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接