.NET Forum / Languages / C# / January 2008
Can a base class method return an object of an inherited class typ
|
|
Thread rating:  |
Ethan Strauss - 09 Jan 2008 20:39 GMT 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 ethan.strauss@promega.com
Nicholas Paldino [.NET/C# MVP] - 09 Jan 2008 21:00 GMT 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.
 Signature - Nicholas Paldino [.NET/C# MVP] - mvp@spam.guard.caspershouse.com
> Hi, > [quoted text clipped - 63 lines] > 800-356-9526 > ethan.strauss@promega.com Peter Duniho - 09 Jan 2008 21:10 GMT > [...] > I want to have the base class have a "Subsquence" method which is [quoted text clipped - 16 lines] > 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(); } } }
Ethan Strauss - 09 Jan 2008 21:46 GMT 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
> > [...] > > I want to have the base class have a "Subsquence" method which is [quoted text clipped - 66 lines] > } > } Peter Duniho - 09 Jan 2008 23:39 GMT > 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
Ethan Strauss - 10 Jan 2008 15:15 GMT 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
> > Thanks Peter. > > I can't get it to compile. [quoted text clipped - 15 lines] > > Pete Peter Duniho - 10 Jan 2008 18:44 GMT > 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
Ethan Strauss - 10 Jan 2008 21:01 GMT 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
> > I can certainly put in the explicit cast to "BiologySequence", but > > then it [quoted text clipped - 32 lines] > > Pete Peter Duniho - 11 Jan 2008 00:50 GMT > 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.
Ethan Strauss - 11 Jan 2008 18:38 GMT 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
Peter Duniho - 11 Jan 2008 19:52 GMT > Maybe I am just being dense, but looking back at your example, again, > does not help. [quoted text clipped - 9 lines] > 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 > [quoted text clipped - 6 lines] > return ToReturn; > } This is what you want.
> which compiles, gives an error of ("Cannot implicitly convert type > 'BiologyTools.BiologySequence' to 'BiologyTools.NucleicAcidSequence'.") [quoted text clipped - 7 lines] > > 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
Ethan Strauss - 11 Jan 2008 20:26 GMT 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
> > Maybe I am just being dense, but looking back at your example, again, > > does not help. [quoted text clipped - 93 lines] > > Pete Peter Duniho - 12 Jan 2008 00:41 GMT > 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
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|