C# 为什么要强制转换为接口?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/619716/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 10:31:36  来源:igfitidea点击:

Why cast to an interface?

c#interfacecasting

提问by eft

In Jesse Liberty's Programming C# (p.142) he provides an example where he casts an object to an interface.

在 Jesse Liberty 的 Programming C# (p.142) 中,他提供了一个将对象转换为接口的示例。

 interface IStorable
 {
    ...
 }

 public class Document : IStorable
 {
    ...
 }

 ...
 IStorable isDoc = (IStorable) doc;  
 ...

What is the point of this, particularly if the object's class implements the inteface anyway?

这有什么意义,特别是如果对象的类无论如何都实现了接口?

EDIT1: To clarify, I'm interested in the reason for the cast (if any), notthe reason for implementing interfaces. Also, the book is his 2001 First Edition (based on C#1 so the example may not be germane for later versions of C#).

EDIT1:澄清一下,我对强制转换的原因(如果有)感兴趣,而不是实现接口的原因。此外,这本书是他的 2001 年第一版(基于 C#1,因此该示例可能与更高版本的 C# 无关)。

EDIT2: I added some context to the code

EDIT2:我在代码中添加了一些上下文

采纳答案by Jeroen Landheer

There is only one reason when you actually need a cast: When doc is of a base type of an actual object that implements IStorable. Let me explain:

当您真正需要强制转换时,只有一个原因:当 doc 是实现 IStorable 的实际对象的基类型时。让我解释:

public class DocBase
{
  public virtual void DoSomething()
  {

  }
}

public class Document : DocBase, IStorable
{
  public override void DoSomething()
  {
    // Some implementation
    base.DoSomething();
  }

  #region IStorable Members

  public void Store()
  {
    // Implement this one aswell..
    throw new NotImplementedException();
  }

  #endregion
}

public class Program
{
  static void Main()
  {
    DocBase doc = new Document();
    // Now you will need a cast to reach IStorable members
    IStorable storable = (IStorable)doc;
  }
}

public interface IStorable
{
  void Store();
}

回答by James Curran

If the object implements the interface explicitly (public void IStorable.StoreThis(...)) that casting is the easiest way to actually reach the interface members.

如果对象显式地实现了接口 ( public void IStorable.StoreThis(...)),则转换是实际到达接口成员的最简单方法。

回答by Tundey

Because you want to restrict yourself to only methods provided by the interface. If you use the class, you run the risk of calling a method (inadvertently) that's not part of the interface.

因为您想限制自己只能使用接口提供的方法。如果您使用该类,则会冒调用(无意中)不属于接口的方法的风险。

回答by Jon Skeet

It's pretty hard to tell without more of the context. If the variable docis declared to be a type which implements the interface, then the cast is redundant.

如果没有更多的上下文,很难说清楚。如果变量doc被声明为实现接口的类型,那么强制转换是多余的。

Which version of the book are you reading? If it's "Programming C# 3.0" I'll have a look tonight when I'm at home.

你在看哪个版本的书?如果是“Programming C# 3.0”,我今晚在家看一下。

EDIT: As we've seen in the answers so far, there are three potential questions here:

编辑:正如我们在迄今为止的答案中看到的,这里有三个潜在的问题:

  • Why cast in the statement shown in the question? (Answer: you don't have to if docis of an appropriate compile-time type)
  • Why is it ever appropriate to explicitly cast to an implemented interface or base class? (Answer: explicit interface implementation as shown in another answer, and also for the sake of picking a less specific overload when passing the cast value as an argument.)
  • Why use the interface at all? (Answer: working with the interface type means you're less susceptible to changes in the concrete type later on.)
  • 为什么在问题中显示的语句中进行转换?(答案:如果doc是适当的编译时类型,则不必)
  • 为什么显式转换为已实现的接口或基类是合适的?(答案:显式接口实现如另一个答案所示,也是为了在将强制转换值作为参数传递时选择一个不太具体的重载。)
  • 为什么要使用界面?(答案:使用接口类型意味着您以后不太容易受到具体类型更改的影响。)

回答by Christian Klauser

The docobject might be of a type that implements members of IStorableexplicitly, not adding them to the classes primary interface (i.e., they can only be called via the interface).

doc对象可能是这种类型,其的工具部件IStorable明确地,不将其添加到类主接口(即,它们只能通过接口调用)。

Actually "casting" (using the (T) syntax) does not make any sense since C# handles upcasts (cast to parent type) automatically (unlike F# for instance).

实际上“转换”(使用 (T) 语法)没有任何意义,因为 C# 自动处理向上转换(转换为父类型)(例如与 F# 不同)。

回答by royatl

The point is, the object (where did you get it?) may notimplement the interface, in which case an exception is thrown which can be caught and dealt with. Of course you can use the "is" operator to check, and the "as" operator to cast instead of the C-style cast.

关键是,对象(你从哪里得到的?)可能没有实现接口,在这种情况下会抛出一个可以捕获和处理的异常。当然,您可以使用“is”运算符进行检查,并使用“as”运算符进行强制转换,而不是使用 C 样式的强制转换。

回答by Ramesh

I am not sure under what context the example was given in the book. But, you generally can type cast an object to interface to achieve multiple inheritance. I have given the example below.

我不确定书中给出的例子是在什么情况下给出的。但是,您通常可以将对象类型转换为接口以实现多重继承。我已经给出了下面的例子。

public interface IFoo
{
     void Display();
}
public interface IBar
{
     void Display();
}

public class MyClass : IFoo, IBar
{
    void IBar.Display()
    {
        Console.WriteLine("IBar implementation");
    }
    void IFoo.Display()
    {
        Console.WriteLine("IFoo implementation");
    }
}

public static void Main()
{
    MyClass c = new MyClass();
    IBar b = c as IBar;
    IFoo f = c as IFoo;
    b.Display();
    f.Display();
    Console.ReadLine();
}

This would display

这将显示

IBar implementation
IFoo implementation

IBar 实现
IFoo 实现

回答by DevinB

As has been noted, the casting is superfluous and not necessary. However, it is a more explicit form of coding which would be useful to beginners in aiding their understanding.

如前所述,铸造是多余的,也不是必需的。然而,它是一种更明确的编码形式,对初学者帮助他们理解很有用。

In an introductory textbook, it is best to explicitly act, rather than let the compliler do things implicitly, which would be more confusing for beginners.

在入门教材中,最好是明确地行动,而不是让编译者隐性地做事情,这会让初学者更加困惑。

The "doc" is not of type "IStorable" so it would be confusing for beginners to see that it is being assigned to a isDoc. By explicitly casting, the author (of the book and of the code) is saying that a document can be casted to an IStorable object, but it is NOT THE SAME as an IStorable object.

“doc”不是“IStorable”类型,因此初学者看到它被分配给 isDoc 会令人困惑。通过显式转换,作者(本书和代码的作者)表示可以将文档转换为 IStorable 对象,但它与 IStorable 对象不同。

回答by Bill K

There are a lot of good answers here, but I don't really think they answer WHY you actually WANT to use the most restrictive interface possible.

这里有很多很好的答案,但我真的不认为他们回答了为什么您实际上想要使用最严格的界面。

The reasons do not involve your initial coding, they involve the next time you visit or refactor the code--or when someone else does it.

原因不涉及您最初的编码,而是涉及您下次访问或重构代码时——或者当其他人这样做时。

Let's say you want a button and are placing it on your screen. You are getting the button either passed in or from another function, like this:

假设您想要一个按钮并将其放置在屏幕上。您正在获取传入或从另一个函数传入的按钮,如下所示:

Button x=otherObject.getVisibleThingy();
frame.add(x);

You happen to know that VisibleThingy is a button, it returns a button, so everything is cool here (no cast required).

你碰巧知道 VisibleThingy 是一个按钮,它返回一个按钮,所以这里的一切都很酷(不需要强制转换)。

Now, lets say that you refactor VisibleThingy to return a toggle button instead. You now have to refactor your method because you knew too much about the implementation.

现在,假设您重构 VisibleThingy 以返回一个切换按钮。您现在必须重构您的方法,因为您对实现了解太多。

Since you only NEED the methods in Component (a parent of both button and Toggle, which could have been an interface--same thing pretty much for our purposes), if you had written that first line like this:

由于您只需要 Component 中的方法(按钮和 Toggle 的父级,这可能是一个接口——对于我们的目的来说几乎是相同的),如果您像这样编写第一行:

Component x=(Component)otherObject.getVisibleThingy();

You wouldn't have had to refactor anything--it would have just worked.

你不必重构任何东西——它会起作用的。

This is a very simple case, but it can be much more complex.

这是一个非常简单的案例,但它可能要复杂得多。

So I guess the summary would be that an interface is a specific way to "View" your object--like looking at it through a filter...you can only see some parts. If you can restrict your view enough, the object can "Morph" behind your particular view and not effect anything in your current world--a very powerful trick of abstraction.

所以我想总结一下,界面是一种“查看”对象的特定方式——比如通过过滤器查看它……你只能看到一些部分。如果您可以充分限制您的视图,则对象可以在您的特定视图后面“变形”,并且不会影响您当前世界中的任何东西——这是一个非常强大的抽象技巧。

回答by Dean Poulin

The best reason why you would cast to interfaces would be if you are writing code against objects and you don't know what concrete type they are and you don't want to.

您将转换为接口的最佳原因是,如果您正在针对对象编写代码并且您不知道它们是什么具体类型并且您不想知道。

If you know that you might come across an object that implements a specific interface you could then get the values out of the object without having to know the concrete class that this object is. Also, if you know that an object implements a given interface, that interface might define methods that you can execute to take certain actions on the object.

如果您知道您可能会遇到一个实现特定接口的对象,那么您可以从该对象中获取值,而无需知道该对象的具体类。此外,如果您知道一个对象实现了一个给定的接口,那么该接口可能会定义一些方法,您可以执行这些方法来对该对象执行某些操作。

Here's a simple example:

这是一个简单的例子:

public interface IText
{
   string Text { get; }
}

public interface ISuperDooper
{
   string WhyAmISuperDooper { get; }
}

public class Control
{
   public int ID { get; set; }
}

public class TextControl : Control, IText
{
   public string Text { get; set; }
}

public class AnotherTextControl : Control, IText
{
   public string Text { get; set; }
}

public class SuperDooperControl : Control, ISuperDooper
{
   public string WhyAmISuperDooper { get; set; }
}

public class TestProgram
{
   static void Main(string[] args)
   {
      List<Control> controls = new List<Control>
               {
                   new TextControl
                       {
                           ID = 1, 
                           Text = "I'm a text control"
                       },
                   new AnotherTextControl
                       {
                           ID = 2, 
                           Text = "I'm another text control"
                       },
                   new SuperDooperControl
                       {
                           ID = 3, 
                           WhyAmISuperDooper = "Just Because"
                       }
               };

       DoSomething(controls);
   }

   static void DoSomething(List<Control> controls)
   {
      foreach(Control control in controls)
      {
         // write out the ID of the control
         Console.WriteLine("ID: {0}", control.ID);

         // if this control is a Text control, get the text value from it.
         if (control is IText)
            Console.WriteLine("Text: {0}", ((IText)control).Text);

         // if this control is a SuperDooperControl control, get why
         if (control is ISuperDooper)
            Console.WriteLine("Text: {0}", 
                ((ISuperDooper)control).WhyAmISuperDooper);
      }
   }
}

running this little program would give you the following output:

运行这个小程序会给你以下输出:

ID: 1

Text: I'm a text control

ID: 2

Text: I'm another text control

ID: 3

Text: Just Because

编号:1

文字:我是文字控件

编号:2

文本:我是另一个文本控件

编号:3

文字:只是因为

Notice that I didn't have to write any code in the DoSomething method that required me to know anything about all the objects I was working on being concrete object types. The only thing that I know is that I'm working on objects that are at least an instance of the Control class. I can then use the interface to find out what else they might have.

请注意,我不必在 DoSomething 方法中编写任何要求我了解我正在处理的所有对象的具体对象类型的代码。我唯一知道的是,我正在处理至少是 Control 类实例的对象。然后我可以使用该界面找出他们可能还有什么。

There's a million different reasons that you would take this approach with interfaces on your objects but it gives you a loose way to access your objects without having to know exactly what it is.

有数百万种不同的原因让您对对象上的接口采用这种方法,但它为您提供了一种访问对象的松散方式,而无需确切知道它是什么。

Think of all the credit cards in the world, every company makes their own, the interface is the same though, so every card reader can have a card swiped through it that follows the standard. Similar to the usage of interfaces.

想想世界上所有的信用卡,每个公司都有自己的,但界面是一样的,所以每个读卡器都可以刷一张符合标准的卡。类似于接口的使用。