OOPS概念:在C#中传递对象引用和创建类对象有什么区别?

时间:2015-06-01 09:40:22

标签: c# class interface

我有一个班级CustomerNew和一个界面ICustomer

public class CustomerNew : ICustomer
{
    public void A()
    {
        MessageBox.Show("Class method");
    }

    void ICustomer.A()
    {
        MessageBox.Show("Interface method");
    }


    public void B()
    {
        MessageBox.Show("Class Method");
    }
}

public interface ICustomer
{
    void A();
}

我对这两行代码感到很困惑。

ICustomer objnew = new CustomerNew();
CustomerNew objCustomerNew = new CustomerNew();
objnew.B(); // Why this is wrong?
objCustomerNew.B(); // This is correct because we are using object of class

第一行代码意味着我们在objnew传递CustomerNew类的对象引用,我是否正确?如果是,那么为什么我不能使用interface objnew访问类的方法B()?

有人可以详细解释这两个。

6 个答案:

答案 0 :(得分:28)

接口具有许多功能和用途,但一个核心问题是能够在商定的合同中向外界提供功能。

让我举个例子。考虑一个汽水自动售货机。它有一个或两个插槽供您输入硬币,一些按钮供您选择正确类型的苏打水和一个按钮来分配苏打水(除非选择按钮也这样做)。

现在,这是界面。这隐藏了界面背后的机器的复杂性,并为您提供了一些选择。

但是,这里是重要的部分,内部机器具有很多功能,它甚至可能有其他按钮和旋钮,通常用于维护人员测试或操作机器时有些不对劲,或者当他们不得不把钱清空或用苏打水填满时。

这部分机器对您是隐藏的,您只能访问添加到该界面的外部接口的创建者。

您甚至不知道机器在幕后实际操作。我可以创建一个新的自动售货机,从附近的工厂传送苏打水,并将你直接添加的硬币传送到银行,你就不会更聪明了。更不用说我会变得富有,但这是另一个故事。

所以,回到你的代码。

您明确声明objnewICustomer。无论你把放在之后,这个界面都是隐藏的。您只能访问作为该接口的一部分声明的任何内容

另一个变量被声明为具有底层对象的类型,因此您可以完全访问其所有公共功能。可以把它想象成解锁自动售货机并在正面打开时使用它。

答案 1 :(得分:7)

在第一行:

ICustomer objnew

您指示编译器将objnew视为ICustomer,并且由于接口未声明B()方法,因此错误。

在第二行:

CustomerNew objCustomerNew

您将objCustomerNew称为CustomerNew,因为它确实指定了B()方法,所以它编译得很好。

答案 2 :(得分:7)

实际上接口也是一种类型(你不能创建接口实例,因为它们只是元数据)。

由于CustomerNew实施ICustomerCustomerNew的实例可以上传ICustomer。当CustomerNew输入ICustomer时,您只能访问ICustomer个成员。

这是因为C#是一种强类型语言,因此,为了访问特定成员(即方法,属性,事件......),您需要一个对象引用,以便通过定义成员的类型进行限定想要访问(即您需要将CustomerNew对象存储在CustomerNew类型的引用中以访问方法B

更新

OP说:

  

因此,由于向上转换,我们只能访问内部的那些方法   界面,对吗? UPCAST​​ING是背后的主要原因吗?

是。一个简单的解释是实现ICustomer的对象不应强制为CustomerNew。您需要向下转发 ICustomerCustomerNew的引用才能访问CustomerNew成员。

由于类和接口都是类型,并且C#是强类型语言,因此可以通过提供其实际类型来访问对象成员。这就是您需要使用强制转换的原因。

例如,您的代码执行隐式上传:

// This is an implicit cast that's equivalent to
// ICustomer objnew = (ICustomer)new CustomerNew()
ICustomer objnew = new CustomerNew();

隐式upcast是可能的,因为编译器已经知道CustomerNew实现ICustomer内省CustomerNew元数据,而你不能暗示 - 向下转换 a {{1引用,因为谁知道谁实现了ICustomer?它可以是ICustomer或任何其他类,甚至是结构:

CustomerNew

如果您不确定ICustomer asInterface = new CustomerNew(); // ERROR: This won't compile, you need to provide an EXPLICIT DOWNCAST CustomerNew asClass1 = asInterface; // OK. You're telling the compiler you know that asInterface // reference is guaranteed to be a CustomerNew too! CustomerNew asClass2 = (CustomerNew)asInterface; 是否为ICustomer,则可以使用CustomerNew运算符,如果转换为运行时不会在运行时抛出异常不可能:

as

答案 3 :(得分:4)

这与编译时(即静态)类型检查有关。如果你看两行

ICustomer objnew = new CustomerNew();
objnew.B(); 

您可以清楚地看到objnew引用的对象具有B()方法,因此您知道在第二行的运行时没有问题。

但是,这不是编译器如何看待它。编译器使用一组相当简单的规则来确定是否报告错误。当它看起来是一个呼叫objnew.B()时,它使用静态(即声明的)类型objnew来确定接收者的类型。静态(声明的)类型为ICustomer,并且该类型不会声明B()方法,因此报告了错误。

那么为什么编译器会忽略给予引用的初始值?这有两个原因:

第一:因为它一般不能使用这些信息,因为 - 通常 - 可能有干预的条件分配。例如,您可能拥有类似于此的代码

ICustomer objnew = new CustomerNew();
if( some complicated expression ) objnew = new CustomerOld() ;
objnew.B(); 

其中CustomerOld是实现接口但没有B()方法的类。

第二:可能没有初始化表达式。这尤其发生在参数上。当编译器编译方法时,它无法访问该方法的所有调用集。考虑

void f( ICustomer objnew ) {
    objnew.B(); 
}

为了使规则简单,对于(其他)变量,参数使用相同的规则。

您当然可以想象一种规则不同的语言,但这就是C#,Java和类似语言的工作方式。

答案 4 :(得分:3)

您的objnew变量是对实现ICustomer的对象的引用。请注意,这可能是任何对象,只要它实现ICustomer即可。因此,此引用所揭示的内容都是ICustomer的成员,它只是您示例中的方法A()

如果您确实想要访问B()引用的对象的objnew方法,则必须将其显式转换为CustomerNew引用(这是C#的类型安全性)工作),例如的是:

CustomerNew objCustomerNew = objnew as CustomerNew;
objCustomerNew.B();

(objnew as CustomerNew).B();

请注意,objnew可以是实现ICustomer的任何类型,因此如果您实施其他objnew as CustomerNew类,转化null可以解析为ICustomer后面。

答案 5 :(得分:1)

简单来说,我会说 objnew ICustomer 类型的变量,其中包含 CustomerNew 类型的引用。由于 ICustomer 没有编译器在编译时检查的方法 B()的声明或定义(它不能作为接口)它会抛出编译类型错误。

现在进行解释,在编译时编译器检查变量类型的MethodDef表,即 ICustomer ,并向上推进类定义的层次结构级别。如果它在定义表中找到方法,它将引用已找到的方法(这又取决于其他方案,如我们可以在其他主题中讨论的覆盖)。我们可以使用一个我想在这里讨论的简单示例来理解

public interface IMyInterface
{

}

public class MyBaseClass:IMyInterface
{
    public string B()
    {
        return "In Abstract";
    }
}

public class MyDerivedClass : MyBaseClass
{

}

请通过上面代码片段中的类层次结构。现在进入实现部分,如下所示,

        MyBaseClass inst = new MyDerivedClass();
        inst.B(); //Works fine 

        MyDerivedClass inst1 = new MyDerivedClass();
        inst1.B(); //Works fine

        IMyInterface inst2 = new MyDerivedClass();
        inst2.B(); //Compile time error

场景1工作正常,因为我之前已经解释过, inst MyBaseClass 类型的变量,其中包含 MyDerivedClass <类型的引用/ strong>和 MyBaseClass 定义了方法B()。

现在进入方案2,它也可以正常运行但如何? inst1 MyDerivedClass 类型的变量,其中包含类型为 MyDerivedClass 的变量,但方法B()未在此类型中定义,但是编译器会在编译时检查 MyDerivedClass 的MethodDef表,并发现 MyBaseClass 在运行时引用的方法B()中有定义。

所以到现在为止,我希望你明白我的观点,这就解释了为什么方案3不起作用。