在调用Dispose()之前将其转换为IDisposable

5
在调用 Dispose() 之前,为什么要将对象转换为 IDisposable
public interface ITransaction : IDisposable
{}
.
.
.

//in some other class:
public void EndTransaction(ITransaction transaction)
{
     if (transaction != null)
     {
          (transaction as IDisposable).Dispose(); 
          // is the following code wrong? transaction.Dispose()

          transaction = null;
     }
}

这是ITransaction的具体实现之一:
public class NHibernateTransaction : ITransaction
{

     public NHibernateTransaction(NHibernate.ITransaction transaction)
     {
          this.Transaction = transaction;
     }

     protected NHibernate.ITransaction Transaction { get; private set; }

     public void Dispose()
     {
         if ( this.Transaction != null )
         {
             (this.Transaction as IDisposable).Dispose(); // this is NHibernate  ITransaction object
              this.Transaction = null;
         }
      }

我在一个开源实现的仓储模式中多次看到了这段代码片段,但似乎无法理解强制类型转换的原因。直接在if语句内部调用transaction.Dispose()应该完全没有问题。我错过了什么吗?

原始代码可以在此处找到:NHibernateTransaction.cs


6
这很不对。赋予空值也是错误的。否则它将不正常工作。写这个东西的人并不真正了解.NET。 - Hans Passant
这是你的答案 https://dev59.com/wG445IYBdhLWcg3wytIU - Andrew T Finnell
1
@Hans:它们都是“不必要的”。你可以称之为错误的风格,但它们实际上并不会造成伤害。不过在“不太理解.NET”这一点上我同意。 - H H
@Henk - 嗯,这对毫无防备的新手程序员来说是有害的。这有点不对。赞扬yanong的指出。 - Hans Passant
如果你发布的代码确实是原始代码的样子,那么Oded对他们的意图是正确的。但尽管如此,它仍然是不必要的。也许有些了解.NET 1.0到4.0年间变化的人可以解释一下,你过去可能需要这样做。 - Andrew T Finnell
显示剩余3条评论
3个回答

3

由于ITransaction继承自IDisposable,因此实现者可能已将IDisposable实现为显式接口实现,在这种情况下需要进行强制转换才能访问已实现的成员。

在这种情况下,强制转换确保调用IDisposable.Dispose方法。进行强制转换是为了涵盖所有可能性。

如果ITransaction没有继承IDisposable,但实现者继承了它,则需要进行强制转换才能调用Dispose。如果实现者没有实现IDisposable,则此类情况可能会失败(抛出异常)。


1
@Oded 除非事务从IDisposable继承,否则在调用.Dispose()时会抛出Null异常。所以这仍然是错误的。我会质疑它是否被正确地剪切和粘贴。 - Andrew T Finnell
1
@Andrew,也不完全正确。实现类可以同时实现 IDisposableITransaction ,在这种情况下,类型转换可能成功。 - JBSnorro
@JBSnorro,你可能错过了我的一句话,即当它不继承时。我没有说ITransaction不继承。当实现在链中的某个点上不从IDisposable继承时,该代码将抛出异常,并且没有检查。 - Andrew T Finnell
你的编辑让情况变得更糟:类的显式实现在这里并不起作用。参数是 ITransaction - H H
@Andrew,这就是我担心的。如果客户端开发人员不确定交易类型,通过进行强制转换然后调用Dispose(),很有可能会出现空异常。另一方面,如果客户端开发人员确定交易实现IDisposable,则转换是无意义的。 - yanong_banikanhon
@Oded 我现在明白你要表达的观点了。然而,如果你采用我在答案中更新的代码,我无法意外地调用另一个显式实现的Dispose()。 - Andrew T Finnell

0

我不常这样做,但是我删除了整个原始答案,因为我现在明白Oded想要表达的意思了。他们的目的是解决这个问题:

namespace StackOverflow7051864
{
    using System;

    public interface ITransaction : IDisposable {}

    public interface ITryToConfuseDispose
    {
        void Dispose();
    }

    public class Transaction : ITransaction, ITryToConfuseDispose
    {
        void IDisposable.Dispose()
        {
            Console.WriteLine("Happy");
        }

        void ITryToConfuseDispose.Dispose()
        {
            Console.WriteLine("Confused");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            EndTransaction(new Transaction());
        }

        public static void EndTransaction(ITransaction transaction)
        {
            (transaction as IDisposable).Dispose();

            transaction.Dispose();            
        }
    }
}

'transaction.Dispose()'会调用什么?它会调用IDisposable.Dispose()

问题在于,由于EndTransaction的合同是ITransaction,因此它将始终调用IDisposable版本。


如果“ITransaction”定义了自己的“Dispose”方法,会怎样? - Oded
1
@Oded,ITransaction本身没有Dispose方法。Dispose()方法是在具体的类中实现的(例如NHibernateTransaction)。我很快就会在我的原始问题中附上相关代码。 - yanong_banikanhon
1
@Oded,这确实会引起问题,但我认为您仍然不应该将其转换为IDisposable来调用Dispose()。ITransaction的实现者必须在Dispose()方法上使用“new”关键字,这意味着他们明确地希望调用不同的Dispose()。我认为提供任何实际场景,说明这种做法是实用和安全的,都将极大地有益于这个问题。 - Andrew T Finnell

0

当在继承自IDisposable的接口上调用Dispose()时,没有区别。

当在实现IDisposable的类上调用Dispose()时,可能会有所不同,因为方法Dispose()可能被显式实现。


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