我有一个班级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()?
有人可以详细解释这两个。
答案 0 :(得分:28)
接口具有许多功能和用途,但一个核心问题是能够在商定的合同中向外界提供功能。
让我举个例子。考虑一个汽水自动售货机。它有一个或两个插槽供您输入硬币,一些按钮供您选择正确类型的苏打水和一个按钮来分配苏打水(除非选择按钮也这样做)。
现在,这是界面。这隐藏了界面背后的机器的复杂性,并为您提供了一些选择。
但是,这里是重要的部分,内部机器具有很多功能,它甚至可能有其他按钮和旋钮,通常用于维护人员测试或操作机器时有些不对劲,或者当他们不得不把钱清空或用苏打水填满时。
这部分机器对您是隐藏的,您只能访问添加到该界面的外部接口的创建者。
您甚至不知道机器在幕后实际操作。我可以创建一个新的自动售货机,从附近的工厂传送苏打水,并将你直接添加的硬币传送到银行,你就不会更聪明了。更不用说我会变得富有,但这是另一个故事。
所以,回到你的代码。
您明确声明objnew
为ICustomer
。无论你把放在之后,这个界面都是隐藏的。您只能访问作为该接口的一部分声明的任何内容。
另一个变量被声明为具有底层对象的类型,因此您可以完全访问其所有公共功能。可以把它想象成解锁自动售货机并在正面打开时使用它。
答案 1 :(得分:7)
在第一行:
ICustomer objnew
您指示编译器将objnew
视为ICustomer
,并且由于接口未声明B()
方法,因此错误。
在第二行:
CustomerNew objCustomerNew
您将objCustomerNew
称为CustomerNew
,因为它确实指定了B()
方法,所以它编译得很好。
答案 2 :(得分:7)
实际上接口也是一种类型(你不能创建接口实例,因为它们只是元数据)。
由于CustomerNew
实施ICustomer
,CustomerNew
的实例可以上传到ICustomer
。当CustomerNew
输入ICustomer
时,您只能访问ICustomer
个成员。
这是因为C#是一种强类型语言,因此,为了访问特定成员(即方法,属性,事件......),您需要一个对象引用,以便通过定义成员的类型进行限定想要访问(即您需要将CustomerNew
对象存储在CustomerNew
类型的引用中以访问方法B
)。
因此,由于向上转换,我们只能访问内部的那些方法 界面,对吗? UPCASTING是背后的主要原因吗?
是。一个简单的解释是实现ICustomer
的对象不应强制为CustomerNew
。您需要向下转发 ICustomer
对CustomerNew
的引用才能访问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不起作用。