简体   繁体   English

风格的不同:IDictionary与Dictionary

[英]A difference in style: IDictionary vs Dictionary

I have a friend who's just getting into .NET development after developing in Java for ages and, after looking at some of his code I notice that he's doing the following quite often: 我有一个朋友刚刚开始使用Java开发.NET开发很长时间,在查看了他的一些代码后,我注意到他经常做以下事情:

IDictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();

He's declaring dictionary as the Interface rather than the Class. 他将字典声明为接口而不是类。 Typically I would do the following: 通常我会做以下事情:

Dictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();

I'd only use the IDictionary interface when it's needed (say, for example to pass the dictionary to a method that accepts an IDictionary interface). 我只在需要时使用IDictionary接口(例如,将字典传递给接受IDictionary接口的方法)。

My question is: are there any merits to his way of doing things? 我的问题是:他的做事方式有什么优点吗? Is this a common practice in Java? 这是Java中的常见做法吗?

If IDictionary is a "more generic" type than Dictionary then it makes sense to use the more generic type in declaring variables. 如果IDictionary是一个比Dictionary更“通用”的类型,那么在声明变量时使用更通用的类型是有意义的。 That way you don't have to care as much about the implementing class assigned to the variable and you can change the type easily in the future without having to change a lot of following code. 这样您就不必太在意分配给变量的实现类,并且您可以在将来轻松更改类型,而无需更改大量以下代码。 For example, in Java it's often considered better to do 例如,在Java中,通常认为更好

List<Integer> intList=new LinkedList<Integer>();

than it is to do 比它要做的

LinkedList<Integer> intList=new LinkedList<Integer>();

That way I'm sure all following code treats the list as a List and not a LinkedList, making it easy in the future to switch out LinkedList for Vector or any other class which implements List. 这样我确定所有后续代码都将列表视为List而不是LinkedList,这样可以在将来轻松切换LinkedList for Vector或任何其他实现List的类。 I'd say this is common to Java and good programming in general. 我会说这对Java来说很常见,而且编程也很好。

This practice isn't just limited to Java. 这种做法不仅限于Java。

It's often used in .NET as well when you want to de-couple the instance of the object from the class you're using. 当你想要从你正在使用的类中解除对象的实例时,它也经常在.NET中使用。 If you use the Interface rather than the Class, you can change the backing type whenever needed without breaking the rest of your code. 如果使用Interface而不是Class,则可以在需要时更改支持类型,而不会破坏其余代码。

You'll also see this practice used heavily with dealing with IoC containers and instanciation using the Factory pattern. 您还会看到这种做法在使用Factory模式处理IoC容器和实例化时会大量使用。

Your friend is following the very useful principle: 你的朋友遵循这个非常有用的原则:

"Abstract yourself from implementation details" “从实现细节中抽象出自己”

For local variables and private fields, which are already implementation details, it's better to use concrete types than interfaces for the declarations because the concrete classes offer a performance boost (direct dispatch is faster than virtual/interface dispatch). 对于已经是实现细节的局部变量和私有字段,最好使用具体类型而不是声明的接口,因为具体类提供了性能提升(直接调度比虚拟/接口调度更快)。 The JIT will also be able to more easily inline methods if you don't unnecessarily cast to interface types in the local implementation details. 如果您没有在本地实现细节中不必要地转换为接口类型,JIT也将能够更容易地内联方法。 If an instance of a concrete type is returned from a method that returns an interface, the cast is automatic. 如果从返回接口的方法返回具体类型的实例,则转换是自动的。

You should always attempt to program to the interface rather than the concrete class. 您应该始终尝试编程到接口而不是具体类。

In Java or any other object oriented programming language. 在Java或任何其他面向对象的编程语言中。

In .NET world is common to use an I to indicate that is an interface what your're using. 在.NET中,通常使用I表示这是您正在使用的接口。 I think this is more common because in C# they don't have implements and extends to refer class vs interface inheritance. 我认为这更常见,因为在C#中它们没有implements并且extends为引用类和接口继承。

I think whey would type 我想乳清会输入

 class MyClass:ISome,Other,IMore 
 { 
 }

And you can tell ISome an IMore are interfaces while Other is a class 你可以告诉ISome IMore是接口,而Other是一个类

In Java there is no need for such a thing 在Java中,不需要这样的东西

 class MyClass extends Other implements Some, More {
 }

The concept still applies, you should try to code to the interface. 这个概念仍然适用,您应该尝试编写接口。

As far as I've seen Java developers tend to use abstraction (and design patterns) more often than .NET developers. 据我所知,Java开发人员倾向于比.NET开发人员更频繁地使用抽象(和设计模式)。 This seems another example of it: why declare the concrete class when he'll essentially only be working with the interface members? 这似乎是另一个例子:为什么在他基本上只使用接口成员时声明具体类?

Most often, you see the interface type (IDictionary) used when the member is exposed to external code, whether that be outside the assembly or just outside the class. 大多数情况下,您会看到当成员公开外部代码时使用的接口类型(IDictionary),无论是在程序集外还是在类外部。 Typically, most developers use the concrete type internally to a class definition while they expose an encapsulated property using the interface type. 通常,大多数开发人员在内部使用具体类型来定义类定义,同时使用接口类型公开封装属性。 In this way, they can leverage the concrete type's capabilities, but if they change the concrete type, the declaring class's interface doesn't need to change. 通过这种方式,他们可以利用具体类型的功能,但如果他们更改具体类型,则声明类的接口不需要更改。

public class Widget
{
    private Dictionary<string, string> map = new Dictionary<string, string>();
    public IDictionary<string, string> Map
    {
        get { return map; }
    }
}

later can become: 以后可以成为:

class SpecialMap<TKey, TValue> : IDictionary<TKey, TValue> { ... }

public class Widget
{
    private SpecialMap<string, string> map = new SpecialMap<string, string>();
    public IDictionary<string, string> Map
    {
        get { return map; }
    }
}

without changing Widget's interface and having to change other code already using it. 无需更改Widget的界面,也不必更改已使用它的其他代码。

In the described situation almost every Java developer would use the interface to declare the variable. 在所描述的情况下,几乎每个Java开发人员都会使用该接口来声明变量。 The way the Java collections are used is probably one of the best examples: Java集合的使用方式可能是最好的例子之一:

Map map = new HashMap();
List list = new ArrayList();

Guess it just accomplishes loose coupling in a lot of situations. 猜猜它只是在很多情况下完成松耦合。

Java Collections include a multitude of implementations. Java Collections包含大量实现。 Therefore, it's much easier for me to make use of 因此,我更容易使用

List<String> myList = new ArrayList<String>();

Then in the future when I realize I need "myList" to be thread safe to simply change this single line to 然后在将来我意识到我需要“myList”是线程安全的,只需将此单行更改为

List<String> myList = new Vector<String>();

And change no other line of code. 并且不改变其他代码行。 This includes getters/setters as well. 这包括getter / setter。 If you look at the number of implementations of Map for example, you can imagine why this might be good practice. 例如,如果你看一下Map的实现数量,你可以想象为什么这可能是一个好习惯。 In other languages, where there is only a couple implementations for something (sorry, not a big .NET guy) but in Objective-C there is really only NSDictionary and NSMutableDictionary. 在其他语言中,只有几个实现的东西(对不起,不是一个大的.NET人),但在Objective-C中,实际上只有NSDictionary和NSMutableDictionary。 So, it doesn't make as much sense. 所以,它没有那么多意义。

Edit: 编辑:

Failed to hit on one of my key points (just alluded to it with the getter/setters). 无法点击我的一个关键点(只是用getter / setter提到它)。

The above allows you to have: 以上允许您:

public void setMyList(List<String> myList) {
    this.myList = myList;
}

And the client using this call need not worry about the underlying implementation. 使用此调用的客户端无需担心底层实现。 Using whatever object that conforms to the List interface that they may have. 使用符合List接口的任何对象。

Coming from a Java world, I agree that the "program to an interface" mantra is drilled into you. 来自Java世界,我同意将“程序到界面”的咒语刻录到你身上。 By programming to an interface, not an implementation, you make your methods more extensible to future needs. 通过编程到接口而不是实现,您可以使您的方法更具可扩展性,以满足未来的需求。

I've found that for local variables it generally doesn't much matter whether you use the interface or the concrete class. 我发现对于局部变量,使用接口或具体类通常没什么关系。

Unlike class members or method signatures, there is very little refactoring effort if you decide to change types, nor is the variable visible outside its usage site. 与类成员或方法签名不同,如果您决定更改类型,则很少有重构工作,变量在其使用站点之外也不可见。 In fact, when you use var to declare locals, you are not getting the interface type but rather the class type (unless you explicitly cast to the interface). 实际上,当您使用var来声明locals时,您没有获得接口类型,而是获取类类型(除非您明确地转换为接口)。

However, when declaring methods, class members, or interfaces, I think that it will save you quite a bit of headache to use the interface type up front, rather than coupling the public API to a specific class type. 但是,在声明方法,类成员或接口时,我认为预先使用接口类型会比较困难,而不是将公共API耦合到特定的类类型。

Using interfaces means that "dictionary" in the following code might be any implementation of IDictionary. 使用接口意味着以下代码中的“字典”可能是IDictionary的任何实现。

Dictionary1 dictionary = new Dictionary1();
dictionary.operation1(); // if operation1 is implemented only in Dictionary1() this will fail for every other implementation

It's best seen when you hide the construction of the object: 隐藏对象的构造时最好看到:

IDictionary dictionary = DictionaryFactory.getDictionary(...);

I've encountered the same situation with a Java developer. 我遇到过与Java开发人员相同的情况。 He instantiates collections AND objects to their interface in the same way. 他以相同的方式将集合AND对象实例化到它们的接口。 For instance, 例如,

IAccount account = new Account();

Properties are always get/set as interfaces. 属性始终作为接口获取/设置。 This causes problems with serialization, which is explained very well here 这会导致序列化问题,这在这里得到了很好的解释

IDictionary is an interface and Dictionary is a class. IDictionary是一个接口, Dictionary是一个类。

Dictionary implements IDictionary . Dictionary实现了IDictionary

That means that this is possible to refer to Dictionary instance with/by IDictionary instance and invoke most of the Dictionary methods and properties through IDictionary instance. 这意味着,这是可能的指Dictionary通过实例与/ IDictionary实例并调用大部分的Dictionary通过方法和属性IDictionary实例。

This is very recommended to use interfaces as many as possible, because interfaces abstracts the modules and assemblies of the applications, allows polymorphism, which is both very common and useful in many situations and cases and allows replacing one module by another without touching the other modules. 建议尽可能多地使用接口,因为接口抽象了应用程序的模块和程序集,允许多态,这在许多情况和情况下都非常常见和有用,并允许在不触及其他模块的情况下将一个模块替换为另一个模块。

Suppose that in the present, the programmer wrote: 假设在目前,程序员写道:

IDictionary<string> dictionary = new Dictionary<string>();

And now dictionary invokes the methods and properties of Dictionary<string> . 现在dictionary调用Dictionary<string>的方法和属性。

In the future the databases has been grown up in size and we find out that Dictionary<string> is too slow, so we want to replace Dictionary<string> by RedBlackTree<string> , which is faster. 将来,数据库的大小已经增长,我们发现Dictionary<string>太慢了,所以我们想要用RedBlackTree<string>替换Dictionary<string> RedBlackTree<string> ,这样会更快。

So all what is needed to be done is replacing the above instruction to: 因此,所有需要做的就是将上述指令替换为:

IDictionary<string> dictionary = new RedBlackTree<string>();

Of course that if RedBlackTree implements IDictionary then all the code of the application compiles successfully and you have a newer version of your application, where the application now performs faster and more efficient than the previous version. 当然,如果RedBlackTree实现了IDictionary那么应用程序的所有代码都会成功编译,并且您有一个较新版本的应用程序,其中应用程序现在比以前的版本执行速度更快,效率更高。

Without interfaces, this replacement would be more difficult to do and would require the programmers and developers to change more code that is potential to bugs. 如果没有接口,这种替换将更加困难,并且需要程序员和开发人员更改可能存在错误的更多代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM