Reputation: 5209
I have a class, CustomerNew
, and an interface, 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();
}
I am very confused between these two code of lines.
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
The first line of code means we are passing object reference of CustomerNew class in objnew
, am I correct? If yes, then why can I not access method B() of the class with interface objnew
?
Can someone explain about these two in detail.
Upvotes: 12
Views: 1801
Reputation: 1627
In Simple words I would say that objnew is a variable of type ICustomer which contains a reference of type CustomerNew. Since ICustomer doesn't have the declaration or definition(which it can't have as it is an interface) of the method B() which compiler checks at compile time and it throws a compile type error.
Now coming to the explanation, at compile time compiler checks the MethodDef table of the Type of the variable i.e. ICustomer and walks up the hierarchy level of class definition. If it finds the method in the definition table it would refer to the method which has been found(which again depend on other scenarios like overriding which we can discuss in other topic). This we can understand using a simple example which I want to discuss here
public interface IMyInterface
{
}
public class MyBaseClass:IMyInterface
{
public string B()
{
return "In Abstract";
}
}
public class MyDerivedClass : MyBaseClass
{
}
Kindly go though the class hierarchy in the above code snippet. Now coming to the implementation part as shown below,
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
Scenario 1 works fine for the obvious reason as I have explained earlier which is, inst is variable of type MyBaseClass which contains a reference of type MyDerivedClass and MyBaseClass has definition of method B().
Now coming to scenario 2, it also works fine but how? inst1 is a variable of type MyDerivedClass which contains a variable again of type MyDerivedClass but method B() is not defined in this type, but what compiler does is it checks at compile time the MethodDef table of MyDerivedClass and finds that MyBaseClass has definition if the Method B() which it refers at run time.
So by now I hope you got my point which explains why scenario 3 will not work.
Upvotes: 1
Reputation: 16241
This has everything to do with compile-time (i.e. static) type checking. If you look at the two lines
ICustomer objnew = new CustomerNew();
objnew.B();
you can clearly see that the object that objnew
refers to has a B()
method, so you know that there would be no problem at run time with the second line.
But, that is not how the compiler looks at it. The compiler uses a set of fairly simple rules to figure out whether to report an error. When it looks a the call objnew.B()
it uses the static (i.e. declared) type of objnew
to determine the type of the recipient. The static (declared) type is ICustomer
, and that type doesn't declare a B()
method, so there is an error reported.
So why does the compiler ignore the initial value given to the reference? Here are two reasons:
First: Because it can't use that information in general because --in general-- there could be intervening conditional assignments. For example you might have code that looks like this
ICustomer objnew = new CustomerNew();
if( some complicated expression ) objnew = new CustomerOld() ;
objnew.B();
where CustomerOld
is some class that implements the interface but has no B()
method.
Second: There might be no initialization expression. This happens in particular with parameters. When the compiler compiles a method it has no access to the set of all calls to the method. Consider
void f( ICustomer objnew ) {
objnew.B();
}
To keep the rules simple, the same rules are used for parameters as for (other) variables.
You could certainly imagine a language where the rules are different, but this is how C#, Java, and similar languages work.
Upvotes: 4
Reputation: 391536
Interfaces have many features and usages but one central thing is the ability to present functionality to the outside world in an agreed upon contract.
Let me give you an example. Consider a soda vending machine. It has a slot or two for you to input coins, a few buttons for you to choose the right type of soda and a button to dispense the soda (unless the choice button also does that).
Now, this is an interface. This hides the complexity of the machine behind the interface and presents a few choices to you.
But, and here is the important part, internally the machine has a lot of functionality and it may even have other buttons and knobs, typically there for the maintenance people to test or operate the machine when there is something wrong, or when they have to empty it for money or fill it with soda.
This part of the machine is hidden from you, you only get access to whatever the creators of the external interface added to that interface.
You don't even know how the machine actually operates behind the scenes. I could create a new vending machine that teleports sodas in from a nearby factory and teleports the coins you added directly to the bank and you would be none the wiser. Not to mention that I would be rich, but that's another story.
So, back to your code.
You explicitly declared objnew
as ICustomer
. Whatever you put behind this interface is hidden. You only get access to whatever it is that is declared as part of that interface.
The other variable was declared as having the type of the underlying object, as such you have full access to all of its public functionality. Think of it like unlocking the vending machine and using it with the front open.
Upvotes: 28
Reputation: 64943
Actually an interface is also a type (you can't create instances of interfaces since they'are just metadata).
Since CustomerNew
implements ICustomer
, an instance of CustomerNew
can be upcasted to ICustomer
. When a CustomerNew
is typed as ICustomer
you can only access ICustomer
members.
This is because C# is a strongly-typed language, thus, in order to access a particular member (i.e. methods, properties, events...) you need an object reference to be qualified by the type which defines the members you want to access (i.e. you need to store CustomerNew
object in a reference of type CustomerNew
to access the method B
).
OP said:
So due to upcasting we can only access those methods which are inside interface, correct ? UPCASTING is the main reason behind this ?
Yes. An easy explanation is an object which implements ICustomer
shouldn't mandatorily be CustomerNew
. You need to downcast an ICustomer
reference to CustomerNew
to be able to access CustomerNew
members.
As both classes and interfaces are types, and C# is a strongly-typed language, you access object members by providing its actual type. This is why you need to use casts.
For example, your code does an implicit upcast:
// This is an implicit cast that's equivalent to
// ICustomer objnew = (ICustomer)new CustomerNew()
ICustomer objnew = new CustomerNew();
Implicit upcasts are possible because the compiler already knows CustomerNew
implements ICustomer
introspecting CustomerNew
metadata, while you can't implictly-downcast a ICustomer
reference because who knows who implements ICustomer
? It can be either CustomerNew
or any other class or even an struct:
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;
If you're not sure that an ICustomer
is a CustomerNew
, you can use the as
operator, which won't throw an exception during run-time if the cast isn't possible:
// If asInterface isn't also a CustomerNew, the expression will set null
CustomerNew asClass3 = asInterface as CustomerNew;
Upvotes: 7
Reputation: 1376
Your objnew
variable is a reference to an object that implements ICustomer
. Be aware of the fact, that this could be any object, as long as it implements ICustomer
. So all that is exposed by this reference, are the members of ICustomer, which is only the method A()
in your example.
If you do want to access the B()
method of the object that is referenced by objnew
, you will have to explicitly convert it to a CustomerNew
reference (that's C#'s type safety at work), like e.g. that:
CustomerNew objCustomerNew = objnew as CustomerNew;
objCustomerNew.B();
or
(objnew as CustomerNew).B();
Keep in mind that objnew
could be of any type implementing ICustomer
, thus the conversion objnew as CustomerNew
could resolve to null
if you implement other ICustomer
classes later.
Upvotes: 3
Reputation: 49105
In the first line:
ICustomer objnew
You're instructing the compiler to treat objnew
as ICustomer
and since the interface does not declare a B()
method, it errors.
In the second line:
CustomerNew objCustomerNew
You're referring to objCustomerNew
as CustomerNew
and since it does specify a B()
method it compiles just fine.
Upvotes: 7