Gaurav123
Gaurav123

Reputation: 5209

OOPS Concepts: What is the difference in passing object reference to interface and creating class object in C#?

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

Answers (6)

Vikram
Vikram

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

Theodore Norvell
Theodore Norvell

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

Lasse V. Karlsen
Lasse V. Karlsen

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

Matías Fidemraizer
Matías Fidemraizer

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).

Update

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

Peter Brennan
Peter Brennan

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

haim770
haim770

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

Related Questions