Can a base class method return an object of an inherited class typ

E

Ethan Strauss

Hi,

I have a class which "BiologySequence" which looks about like this.

public class BiologySequence
{
private string _Sequence;

public string Sequence
{
get
{
return _Sequence;
}
set
{
_Sequence = value;
}
}

public BiologySequence(string sequence)
{
if (sequence != null)
{
_Sequence = sequence;
}
else
{
_Sequence = string.Empty;
}
}
}

and an inherited class (NucleicAcidSequence) which has no other properties
important for this question.

I want to have the base class have a "Subsquence" method which is
essentially the same as the substring method of string. I have written

public BiologySequence Subsequence(int firstBase, int lastBase)
{
BiologySequence ToReturn = this.MemberwiseClone();
ToReturn.Sequence = _Sequence.Substring(firstBase, lastBase -
firstBase);
return (ToReturn);
}
which, I think works, but it always returns an object of type
BiologySequence regardless of what actual class it is called from.

I have tried all sorts of things to recast the object to be returned as the
same type as the calling type, but I can't find one which works.
Is there a way to do this, or do I need to explicitly write this method for
each inheriting class?

Thanks!
Ethan

Ethan Strauss Ph.D.
Bioinformatics Scientist
Promega Corporation
2800 Woods Hollow Rd.
Madison, WI 53711
608-274-4330
800-356-9526
(e-mail address removed)
 
N

Nicholas Paldino [.NET/C# MVP]

Ethan,

Given the limitations of the constraint system (you won't be able to
call the constructor of the NucleicAcidSequence, since the only constructor
you can create a constraint against is the default parameterless one), you
should probably make a parameterless constructor, and then make your
Subsequence method generic:

public T Subsequence<T>(int firstBase, int lastBase) where T :
BiologySequence, new()
{
// Create the return value.
T retVal = new T();

// Assign the new sequence.
retVal.Sequence = Sequence.Substring(firstBase, lastBase - firstBase);

// Return.
return retVal;
}

Of course, it means that anything that derives from BiologySequence must
have a default parameterless constructor if they want to use the Subsequence
method. If you have other properties, they have to be copied over as well.
The problem here is that if you have a large hierarchy chain, then you will
have to have a lot of case statements which will copy all the applicable
properties, or use reflection, which is probably not the direction you
wanted to head in the first place.
 
P

Peter Duniho

[...]
I want to have the base class have a "Subsquence" method which is
essentially the same as the substring method of string. I have written

public BiologySequence Subsequence(int firstBase, int lastBase)
{
BiologySequence ToReturn = this.MemberwiseClone();
ToReturn.Sequence = _Sequence.Substring(firstBase, lastBase -
firstBase);
return (ToReturn);
}
which, I think works, but it always returns an object of type
BiologySequence regardless of what actual class it is called from.

I have tried all sorts of things to recast the object to be returned as
the
same type as the calling type, but I can't find one which works.
Is there a way to do this, or do I need to explicitly write this method
for
each inheriting class?

Can you provide a complete-but-concise example of code that is written as
you'd like it to work, but which doesn't?

The Object.MemberwiseClone() method will return the same type as the
instance being used. So the code you've posted should do exactly what it
_seems_ like to me that you want to do. If your instance is actually a
NucleicAcidSequence, then calling MemberwiseClone() on that instance
should return you a NucleicAcidSequence, even when called from a method in
BiologySequence.

The method will return a reference typed as BiologySequence, but the
caller should be able to cast to NucleicAcidSequence without any trouble..

Here's a simple program that demonstrates this working:

using System;
using System.Collections.Generic;
using System.Text;

namespace TestCloneBaseClass
{
class Program
{
class A : ICloneable
{
#region ICloneable Members

public object Clone()
{
return this.MemberwiseClone();
}

#endregion
}

class B : A
{
}

static void Main(string[] args)
{
A a = new B();
B b = (B)a.Clone();

Console.WriteLine("Type of b: " + b.GetType().Name);
Console.ReadLine();
}
}
}
 
E

Ethan Strauss

Thanks Peter.
I can't get it to compile.
I get "Cannot implicitly convert type 'object' to
'BiologyTools.BiologySequence'. An explicit conversion exists (are you
missing a cast?)" on the line which contains MemberwiseClone.

Ethan


Peter Duniho said:
[...]
I want to have the base class have a "Subsquence" method which is
essentially the same as the substring method of string. I have written

public BiologySequence Subsequence(int firstBase, int lastBase)
{
BiologySequence ToReturn = this.MemberwiseClone();
ToReturn.Sequence = _Sequence.Substring(firstBase, lastBase -
firstBase);
return (ToReturn);
}
which, I think works, but it always returns an object of type
BiologySequence regardless of what actual class it is called from.

I have tried all sorts of things to recast the object to be returned as
the
same type as the calling type, but I can't find one which works.
Is there a way to do this, or do I need to explicitly write this method
for
each inheriting class?

Can you provide a complete-but-concise example of code that is written as
you'd like it to work, but which doesn't?

The Object.MemberwiseClone() method will return the same type as the
instance being used. So the code you've posted should do exactly what it
_seems_ like to me that you want to do. If your instance is actually a
NucleicAcidSequence, then calling MemberwiseClone() on that instance
should return you a NucleicAcidSequence, even when called from a method in
BiologySequence.

The method will return a reference typed as BiologySequence, but the
caller should be able to cast to NucleicAcidSequence without any trouble..

Here's a simple program that demonstrates this working:

using System;
using System.Collections.Generic;
using System.Text;

namespace TestCloneBaseClass
{
class Program
{
class A : ICloneable
{
#region ICloneable Members

public object Clone()
{
return this.MemberwiseClone();
}

#endregion
}

class B : A
{
}

static void Main(string[] args)
{
A a = new B();
B b = (B)a.Clone();

Console.WriteLine("Type of b: " + b.GetType().Name);
Console.ReadLine();
}
}
}
 
P

Peter Duniho

Thanks Peter.
I can't get it to compile.
I get "Cannot implicitly convert type 'object' to
'BiologyTools.BiologySequence'. An explicit conversion exists (are you
missing a cast?)" on the line which contains MemberwiseClone.

Well, you could look at the sample I posted for reference.

The error is telling you exactly what's wrong. The compiler cannot
implicitly cast the type returned by MemberwiseClone(), which is "object",
to the variable being assigned, which is type "BiologySequence". The
error also tells you that an explicit cast exists and asks you if you
forgot to include it.

Which you did.

Put the cast in and it should work fine.

Pete
 
E

Ethan Strauss

Hi,
I can certainly put in the explicit cast to "BiologySequence", but then it
casts as "BiologySequence" rather that the inherited type if I call it from
an inherited class.
I tried
BiologySequence ToReturn = (this.GetType()) this.MemberwiseClone();
(and some variations) to cast it as the correct type, but I have not be able
to get anything of this sort to work.

Ethan
 
P

Peter Duniho

I can certainly put in the explicit cast to "BiologySequence", but
then it
casts as "BiologySequence" rather that the inherited type if I call it
from
an inherited class.

The method returns a BiologySequence, so there's no point in casting it to
anything else. That's what the caller is going to see, regardless.

The caller can then cast it to an appropriate type when it receives the
return value.

You seem to be under the impression that casting will actually change the
type of the instance. It doesn't. Assuming no implicit type conversion
has been implemented (and in this situation, that is the case), all that
casting is going to do is change the way the _compiler_ views the object..
That's all.

So, when you clone the object, the type returned by the method
MemberwiseClone() is "object", but the instance is already whatever type
the original instance was. Likewise, casting it to something else doesn't
change this...it still remains whatever type the original instance was.
The casting just allows the compiler to know that the instance can be
treated as the newly cast type (an exception would occur if the cast was
invalid at run-time).
I tried
BiologySequence ToReturn = (this.GetType()) this.MemberwiseClone();
(and some variations) to cast it as the correct type, but I have not be
able
to get anything of this sort to work.

Please look at the sample that I posted. In a few short lines it
illustrates everything you need to know about this problem.

Pete
 
E

Ethan Strauss

Hi again,
Actually, your explaination below does help clarify things. It appears
that what I really want (a method called from the base class which, when
called from an inherited class, will relturn an object of that inherited
class type) is not possible. This is all I really need to know. I will have
to write a version of the method for each inherited class.
Ethan
 
P

Peter Duniho

Actually, your explaination below does help clarify things. It appears
that what I really want (a method called from the base class which, when
called from an inherited class, will relturn an object of that inherited
class type) is not possible.

That's not true. It's not only possible, I provided a code example that
does exactly that.
 
E

Ethan Strauss

Hi Peter,
Maybe I am just being dense, but looking back at your example, again,
does not help.

I have tried this as a direct extrapolation of your code, but it doesn't
compile because the memberwise clone does not know it has a .Sequence
property

public object Subsequence(int firstBase, int lastBase)
{
return this.MemberwiseClone().Sequence.Substring(firstBase,
lastBase - firstBase); ;
}


I have tried

public object Subsequence(int firstBase, int lastBase)
{
BiologySequence ToReturn = (BiologySequence)
this.MemberwiseClone();
ToReturn.Sequence = _Sequence.Substring(firstBase, lastBase -
firstBase);
return ToReturn;
}
which compiles, gives an error of ("Cannot implicitly convert type
'BiologyTools.BiologySequence' to 'BiologyTools.NucleicAcidSequence'.") for
the next chunk

NucleicAcidSequence target = new NucleicAcidSequence();

int firstBase = 0;
NucleicAcidSequence expected = null;
NucleicAcidSequence actual;

actual = target.Subsequence(firstBase);


I have tried various other things. None of them work without an *explicit*
recast after calling the Subsequence method. I was trying to avoid this
explicit recast after calling, but I think that is not possible.

Does that make sense? Am I still missing something?
Thanks,
Ethan
 
P

Peter Duniho

Maybe I am just being dense, but looking back at your example, again,
does not help.

I have tried this as a direct extrapolation of your code, but it doesn't
compile because the memberwise clone does not know it has a .Sequence
property

public object Subsequence(int firstBase, int lastBase)
{
return
this.MemberwiseClone().Sequence.Substring(firstBase,
lastBase - firstBase); ;
}

Even if the above code were changed to cast the return from
MemberwiseClone() to the right type, it'd be returning a String, not a
BiologySequence or NucleicAcidSequence. So, no...that's definitely not
what you want.
I have tried

public object Subsequence(int firstBase, int lastBase)
{
BiologySequence ToReturn = (BiologySequence)
this.MemberwiseClone();
ToReturn.Sequence = _Sequence.Substring(firstBase, lastBase -
firstBase);
return ToReturn;
}

This is what you want.
which compiles, gives an error of ("Cannot implicitly convert type
'BiologyTools.BiologySequence' to 'BiologyTools.NucleicAcidSequence'.")
for
the next chunk
NucleicAcidSequence target = new NucleicAcidSequence();

int firstBase = 0;
NucleicAcidSequence expected = null;
NucleicAcidSequence actual;

actual = target.Subsequence(firstBase);

Because you need to cast the return value to match the type you know it to
be. (I'm ignoring the fact that your call to Subsequence() has only one
parameter, even though the declared method has two...I assume your actual
code is more consistent than that).
[...]
I have tried various other things. None of them work without an
*explicit*
recast after calling the Subsequence method. I was trying to avoid this
explicit recast after calling, but I think that is not possible.

You never mentioned a requirement to avoid an explicit recast. All you've
ever said is that you want the method to return an object of the correct
type, and that's exactly what it does. (Again, the type of a given
identifier, whether variable, return value, or whatever, is only a
specifier to the compiler...it does _not_ tell you what the actual type of
the object is).

I have no idea why that's a requirement, nor do I think it's a good one.
Just use the second version of the Subsequence() method that you posted,
and cast the return value.

You can't avoid casting completely, because there's no generic
MemberwiseClone() method. But if you really insist on avoiding it in the
caller, you could do something like this:

public T Subsequence<T>(int firstBase, int lastBase)
{
T ToReturn = (T)this.MemberwiseClone();

ToReturn.Sequence = Sequence.Substring(firstBase, lastBase -
firstBase);

return ToReturn;
}

then:

NucleicAcidSequence actual =
target.Subsequence<NucleicAcidSubsequence>(firstBase, lastBase);

This uses C# generics to allow you to specify a specific type for the
method to use as its return value.

Personally, I wouldn't see the point in writing the method like that, but
you could if you wanted to.

Aside: note that I also changed "_Sequence" to "Sequence". IMHO, if
you're using a property, you should always use the property. Hard-coding
the field the property uses pretty much eliminates one of the main
benefits of making it a property in the first place.

Pete
 
E

Ethan Strauss

Thanks.
I think I have everything working now.
I also think I agree with your comments on avoiding explicit casting. This
is my first major foray into inheritance and I am still working out the
kinks. Thanks for sticking with me
Ethan

Peter Duniho said:
Maybe I am just being dense, but looking back at your example, again,
does not help.

I have tried this as a direct extrapolation of your code, but it doesn't
compile because the memberwise clone does not know it has a .Sequence
property

public object Subsequence(int firstBase, int lastBase)
{
return
this.MemberwiseClone().Sequence.Substring(firstBase,
lastBase - firstBase); ;
}

Even if the above code were changed to cast the return from
MemberwiseClone() to the right type, it'd be returning a String, not a
BiologySequence or NucleicAcidSequence. So, no...that's definitely not
what you want.
I have tried

public object Subsequence(int firstBase, int lastBase)
{
BiologySequence ToReturn = (BiologySequence)
this.MemberwiseClone();
ToReturn.Sequence = _Sequence.Substring(firstBase, lastBase -
firstBase);
return ToReturn;
}

This is what you want.
which compiles, gives an error of ("Cannot implicitly convert type
'BiologyTools.BiologySequence' to 'BiologyTools.NucleicAcidSequence'.")
for
the next chunk
NucleicAcidSequence target = new NucleicAcidSequence();

int firstBase = 0;
NucleicAcidSequence expected = null;
NucleicAcidSequence actual;

actual = target.Subsequence(firstBase);

Because you need to cast the return value to match the type you know it to
be. (I'm ignoring the fact that your call to Subsequence() has only one
parameter, even though the declared method has two...I assume your actual
code is more consistent than that).
[...]
I have tried various other things. None of them work without an
*explicit*
recast after calling the Subsequence method. I was trying to avoid this
explicit recast after calling, but I think that is not possible.

You never mentioned a requirement to avoid an explicit recast. All you've
ever said is that you want the method to return an object of the correct
type, and that's exactly what it does. (Again, the type of a given
identifier, whether variable, return value, or whatever, is only a
specifier to the compiler...it does _not_ tell you what the actual type of
the object is).

I have no idea why that's a requirement, nor do I think it's a good one.
Just use the second version of the Subsequence() method that you posted,
and cast the return value.

You can't avoid casting completely, because there's no generic
MemberwiseClone() method. But if you really insist on avoiding it in the
caller, you could do something like this:

public T Subsequence<T>(int firstBase, int lastBase)
{
T ToReturn = (T)this.MemberwiseClone();

ToReturn.Sequence = Sequence.Substring(firstBase, lastBase -
firstBase);

return ToReturn;
}

then:

NucleicAcidSequence actual =
target.Subsequence<NucleicAcidSubsequence>(firstBase, lastBase);

This uses C# generics to allow you to specify a specific type for the
method to use as its return value.

Personally, I wouldn't see the point in writing the method like that, but
you could if you wanted to.

Aside: note that I also changed "_Sequence" to "Sequence". IMHO, if
you're using a property, you should always use the property. Hard-coding
the field the property uses pretty much eliminates one of the main
benefits of making it a property in the first place.

Pete
 
P

Peter Duniho

I think I have everything working now.
I also think I agree with your comments on avoiding explicit casting.
This
is my first major foray into inheritance and I am still working out the
kinks. Thanks for sticking with me

You're welcome...I'm glad you got it working. For what it's worth, the
inheritance issue and the confusion involved isn't uncommon. I'm still
not sure exactly at what point you went off the tracks, but this wouldn't
be the first time that someone posted here confused about what exactly
casting a reference to an object instance does. :)

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top