Bug? Implementing an Interface 
Author Message
 Bug? Implementing an Interface

I get an error doing something that is clearly done in the implementation of
.Net framwork... really annoying. Can someone PLEASE help me out telling me
what I am missing:

I have an interface:
    public interface IDbCommunicator
    {
        IDataReader ExecuteDataReader(string sql);
    }

and a class that implements that interface (note that the return type is now
SqlDataReader):
    public class SqlServer : IDbCommunicator
    {
        public SqlDataReader ExecuteDataReader(string sql)
        {
            ...
            return theSqlDataReader;
        }
    }

This should definately work, since SqlDataReader as a matter of fact
implements IDataReader!!!!! Right???

When compiling I get the following error:
'<the.namespace>SqlServer' does not implement interface member
'<the.namespace>.IDbCommunicator.ExecuteDataReader(string)'.
<the.namespace>.ExecuteDataReader(string)' is either static, not public, or
has the wrong return type.

The very same type of construct is used in the .Net framework: look at
System.Data.IdbCommand and its definition of ExecuteReader vs
System.Data.SqlClient.SqlCommand and its implementation of ExecuteReader...



Mon, 11 Oct 2004 06:20:08 GMT  
 Bug? Implementing an Interface
Marten,

Quote:
>This should definately work, since SqlDataReader as a matter of fact
>implements IDataReader!!!!! Right???

No, the signature of the implementing method must be the exact same as
the interface. You can't replace any parameter of return value with a
derived type.

What you can do (which is exactly waht SqlCommand does) is to
privately implement the interface method, and then add a public
strongly type method

    public class SqlServer : IDbCommunicator
    {
        IDataReader IDbCommunicator.ExecuteDataReader(string sql)
        {
            return ExecuteDataReader( sql );
        }

        public SqlDataReader ExecuteDataReader(string sql)
        {
            ...
            return theSqlDataReader;
        }

    }

Mattias

===
Mattias Sj?gren (VB MVP)

http://www.msjogren.net/dotnet/



Mon, 11 Oct 2004 06:47:39 GMT  
 Bug? Implementing an Interface

Thanks for the response.

After thinking about this for a few minutes, it seems to me that constructs like that kind of violates the intended use of interfaces. Consider the example below; if I am holding a reference to an IDbCommand (and I don't know what type the instance REALLY is) I should certainly be able to assign ANY IDbConnection (that I also don't know the type of) to that IDbCommand... that's the idea anyway, right??? Well, as the example below shows it doesn't work that nicely...

    private void button1_Click(object sender, System.EventArgs e)
    {
        IDbCommand cmd = new SqlCommand();
        cmd = SetConnection(cmd);

        IDbConnection conn = cmd.Connection;
        MessageBox.Show(conn.ConnectionString);
    }

    private IDbCommand SetConnection(IDbCommand cmd)
    {
        cmd.Connection = GetSomeIDbConnection();
        return cmd;
    }

    private IDbConnection GetSomeIDbConnection()
    {
        //return new SqlConnection("Initial Catalog=Northwind;Data Source=localhost;user id=sa; password=foobar");
        return new OleDbConnection("provider=SQLOLEDB;Initial Catalog=Northwind;Data Source=localhost;user id=sa; password=foobar");
    }



Mon, 11 Oct 2004 09:03:01 GMT  
 Bug? Implementing an Interface
I might be missing something, but I don't really understand why you would
like to implement such a structure:

public interface IDbCommunicator
    {
        IDataReader ExecuteDataReader(string sql);
    }
    public class SqlServer : IDbCommunicator
    {
        public SqlDataReader ExecuteDataReader(string sql)
        {
            ...
            return theSqlDataReader;
        }
    }

By trying this (which luckily your compiler did not allow) you are loosing
the whole advantage of the interfaces. This example might be confusing
because you have two interfaces here: IDbCommunicator and IDataReader. You
are actually loosing the advantage of both interfaces. With your code, you
would loose the flexiblity  because you are returning a concrete class
instead of a IDataReader. So your client code might look like this: (again
this is not possible)

...
IDbCommunicator mycommunicator=new SqlServer(); //notice this is abstract
SqlDataReader datareader=mycommunicator.ExecuteDataReader("slkfjslfkj");
//notice this is a concrete class
...

Now you lost the whole advantage of defining SqlServer as a IDbCommunicator
since you are getting back a SqlDataReader. To illustrate this let us say
you want to use switch the SqlServer with a OleServer and get back a
OleDataReader from the ExecuteReader function. Can you do it by plugging in
a new object? No. You need to change your client code to accept a
OleDataReader and all what is associated with it.

To fix it just write:

public class SqlServer : IDbCommunicator
    {
        public IDataReader ExecuteDataReader(string sql)
        {
            ...
            return (IDataReader)theSqlDataReader;
        }
    }

Hope that helps and I am interested if you have thoughts, arguments about
this
Markus



Thanks for the response.

After thinking about this for a few minutes, it seems to me that constructs
like that kind of violates the intended use of interfaces. Consider the
example below; if I am holding a reference to an IDbCommand (and I don't
know what type the instance REALLY is) I should certainly be able to assign
ANY IDbConnection (that I also don't know the type of) to that IDbCommand...
that's the idea anyway, right??? Well, as the example below shows it doesn't
work that nicely...

    private void button1_Click(object sender, System.EventArgs e)
    {
        IDbCommand cmd = new SqlCommand();
        cmd = SetConnection(cmd);

        IDbConnection conn = cmd.Connection;
        MessageBox.Show(conn.ConnectionString);
    }

    private IDbCommand SetConnection(IDbCommand cmd)
    {
        cmd.Connection = GetSomeIDbConnection();
        return cmd;
    }

    private IDbConnection GetSomeIDbConnection()
    {
        //return new SqlConnection("Initial Catalog=Northwind;Data
Source=localhost;user id=sa; password=foobar");
        return new OleDbConnection("provider=SQLOLEDB;Initial
Catalog=Northwind;Data Source=localhost;user id=sa; password=foobar");
    }



Mon, 11 Oct 2004 12:15:17 GMT  
 Bug? Implementing an Interface


Quote:
> I get an error doing something that is clearly done in the implementation
of
> .Net framwork... really annoying. Can someone PLEASE help me out telling
me
> what I am missing:

> I have an interface:
>     public interface IDbCommunicator
>     {
>         IDataReader ExecuteDataReader(string sql);
>     }

> and a class that implements that interface (note that the return type is
now
> SqlDataReader):
>     public class SqlServer : IDbCommunicator
>     {
>         public SqlDataReader ExecuteDataReader(string sql)
>         {
>             ...
>             return theSqlDataReader;
>         }
>     }

> This should definately work, since SqlDataReader as a matter of fact
> implements IDataReader!!!!! Right???

In order to change the return type like you are trying to do, you need a
feature called "covariant return" which is not in C# (it is in languages
like C++ and Eiffel).

What you *can* do is create your own function that has the return type you
want and then implement the interface function with the interface return
type.  Furthermore, you can do so with the same name, just using explicit
interface implementation.  Try this:

    public class SqlServer : IDbCommunicator
    {
        public SqlDataReader ExecuteDataReader(string sql)
        {
            ...
            return theSqlDataReader;
        }
        public IDataReader IDbCommunicator.ExecuteDataReader(string sql)
        {
            ...
            ExecuteDataReader(sql);
        }
    }

Ken



Mon, 11 Oct 2004 12:31:42 GMT  
 Bug? Implementing an Interface
The reason you would want to implement such a structure is that it should be
up to the user of this library to decide if they want to use it in the more
general way (by treating all objects as the interfaces they implement) and
allow for the easy swap of SqlDataReader for a OleDbDataRead you are
describing. If the user wants to use the library in a more strongly typed
manner you can do that as well. Using the construct suggested by Mattias
Sj?gren allows for this flexability. It is actually very elegant!

Not that it really matters, but Microsoft is using this in the actual
Framework as well just as Mattias is pointing out. For esample, look at
SqlCommand and OleDbCommand and how they implement their own strongly typed
version of ExecuteReader etc...

HOWEVER, the point I was trying to make in my previous posting was that when
a similar construct is attempted to force a more strongly typed parameters,
things can get messed up, as my example shows (you get an
InvalidCastException).

Thanks
M?rten Engblom


Quote:
> I might be missing something, but I don't really understand why you would
> like to implement such a structure:

> public interface IDbCommunicator
>     {
>         IDataReader ExecuteDataReader(string sql);
>     }
>     public class SqlServer : IDbCommunicator
>     {
>         public SqlDataReader ExecuteDataReader(string sql)
>         {
>             ...
>             return theSqlDataReader;
>         }
>     }

> By trying this (which luckily your compiler did not allow) you are loosing
> the whole advantage of the interfaces. This example might be confusing
> because you have two interfaces here: IDbCommunicator and IDataReader. You
> are actually loosing the advantage of both interfaces. With your code, you
> would loose the flexiblity  because you are returning a concrete class
> instead of a IDataReader. So your client code might look like this: (again
> this is not possible)

> ...
> IDbCommunicator mycommunicator=new SqlServer(); //notice this is abstract
> SqlDataReader datareader=mycommunicator.ExecuteDataReader("slkfjslfkj");
> //notice this is a concrete class
> ...

> Now you lost the whole advantage of defining SqlServer as a
IDbCommunicator
> since you are getting back a SqlDataReader. To illustrate this let us say
> you want to use switch the SqlServer with a OleServer and get back a
> OleDataReader from the ExecuteReader function. Can you do it by plugging
in
> a new object? No. You need to change your client code to accept a
> OleDataReader and all what is associated with it.

> To fix it just write:

> public class SqlServer : IDbCommunicator
>     {
>         public IDataReader ExecuteDataReader(string sql)
>         {
>             ...
>             return (IDataReader)theSqlDataReader;
>         }
>     }

> Hope that helps and I am interested if you have thoughts, arguments about
> this
> Markus



> Thanks for the response.

> After thinking about this for a few minutes, it seems to me that
constructs
> like that kind of violates the intended use of interfaces. Consider the
> example below; if I am holding a reference to an IDbCommand (and I don't
> know what type the instance REALLY is) I should certainly be able to
assign
> ANY IDbConnection (that I also don't know the type of) to that
IDbCommand...
> that's the idea anyway, right??? Well, as the example below shows it
doesn't
> work that nicely...

>     private void button1_Click(object sender, System.EventArgs e)
>     {
>         IDbCommand cmd = new SqlCommand();
>         cmd = SetConnection(cmd);

>         IDbConnection conn = cmd.Connection;
>         MessageBox.Show(conn.ConnectionString);
>     }

>     private IDbCommand SetConnection(IDbCommand cmd)
>     {
>         cmd.Connection = GetSomeIDbConnection();
>         return cmd;
>     }

>     private IDbConnection GetSomeIDbConnection()
>     {
>         //return new SqlConnection("Initial Catalog=Northwind;Data
> Source=localhost;user id=sa; password=foobar");
>         return new OleDbConnection("provider=SQLOLEDB;Initial
> Catalog=Northwind;Data Source=localhost;user id=sa; password=foobar");
>     }



Mon, 11 Oct 2004 21:26:04 GMT  
 
 [ 6 post ] 

 Relevant Pages 

1. Problem implementing interface

2. Create instance of unnamed class that implements interface

3. Internal Compiler Error while deriving C# class from a MC++ class implementing a C# interface

4. How to implement such kind of interface?

5. Implementing an interface on a inherited class

6. Implementing interface members as protected?

7. Implementing a COM+ interface in C#

8. Implementing a COM interface

9. Implementing an Interface within a Windows Service

10. Calling explicitly implemented interface method from derived class

11. How Implement IMessageFilter interface In my MFC Project?

12. Finding if a CoClass implements an interface that's derived from another

 

 
Powered by phpBB® Forum Software