views:

768

answers:

6

I am having a problem with the return type of a method.

The method returns a linq object which at present returns type tblAppointment. This method is shown below:

public tblAppointment GetAppointment(int id)
{
    var singleAppointment = (from a in dc.tblAppointments
                                                    where a.appID == id
                                                    select a).SingleOrDefault();
    return singleAppointment;

}

The problem is that tblAppointment is abstract and has many sub types that inherit it. When I try and return an object that is of Type "appointmentTypeA" and call the .GetType() method on it, it gives me the correct sub type, but when i try and access the properties it only allows me to access the parent properties. If i take the object and cast it to a new object of the subtype then it works and lets me access everything i need but it seems messy.

var viewSingleAppointment = appointmentRepos.GetAppointment(appointmentId);

Debug.Write(viewSingleAppointment.GetType()); //returns type i want

if (viewSingleAppointment is tblSingleBirthAppointment)
{
    tblSingleBirthAppointment myApp = (tblSingleBirthAppointment)viewSingleAppointment; //need to do this to access TypeA properties for some reason

}

Edit: I have got this working but I need to use a select statement for each appointment (about 20) and cast them to the appropriate type and retreive the properties and im not sure how to refactor this as it will be used on a few pages we are doing.

A: 

If you are returning reference to a child type that is a parent type, the reference will be of that type and the compiler will not allow you to access any of the child type's members until you cast to that type. This is polymorphism in action :)

The good news is that you are not creating a new object when you cast a reference type - you are simply changing the type of the reference that points to the object you already have thereby giving you access to its members.

Andrew Hare
This is a true statement, but it doesn't help him solve the problem at hand...
Pete Michaud
+1  A: 

You could create another method to encapsulate the cast:

public tblSingleBirthAppointment GetBirthAppointment(int id)
{
    var singleAppointment = GetAppointment(id);

    if (singleAppointment != null)
    {
        return (tblSingleBirthAppointment)singleAppointment;
    }

    return null;
}

That method would break if you tried to use it with an ID that wasn't actually a BirthAppointment though, so you might consider checking.

var viewSingleBirthAppointment = appointmentRepos.GetBirthAppointment(appointmentId);
Pete Michaud
+2  A: 

You could create a generic method :

public T GetAppointment<T>(int id) where T : tblAppointment 
{
    var singleAppointment = dc.tblAppointments.SingleOrDefault(a => a.appID == id);
    return (T)singleAppointment;
}

But then you would need to know the object's actual type before calling it...

Thomas Levesque
+6  A: 

Well, if you're using C# 4 you could use dynamic typing... but if you want to stick to static typing, I suspect the best you can do is provide the expected type as a generic type argument, and get the method to perform the cast for you:

public T GetAppointment<T>(int id) where T : tblAppointment
{
    var singleAppointment = (from a in dc.tblAppointments
                                                    where a.appID == id
                                                    select a).SingleOrDefault();
    return (T) singleAppointment;

}

Call this with:

SpecificAppointment app = GetAppointment<SpecificAppointment>(10);

or use implicit typing:

var app = GetAppointment<SpecificAppointment>(10);

It will throw an exception at execution time if the cast fails.

This assumes the caller knows the appointment type (although they could specify tblAppointment if they don't). Without knowing the appropriate appointment type at compile-time it's hard to see how static typing can do you any more favours, really...

Jon Skeet
var app = GetAppointment<SpecificAppointment>(10);Could I do something like this:var viewSingleAppointment = appointmentRepos.GetAppointment(appointmentId);var app = GetSingleAppointment<viewSingleAppointment.GetType()>(appointmentId);
Andi
@SocialAddict: No, because generic type arguments have to be known at compile time. What good would it do you anyway? If you don't know the appointment type at compile-time, how are you going to know which properties to use?
Jon Skeet
The problem with the above code is that i dont know the type of appointment until i have the typeID which is returned from getAppointment. I can make it work by using a large select statement with something like this in each one:switch (viewSingleAppointment.tblAppointmentType.appTypeID){case 1:{tblSingleBirthAppointment myApp = (tblSingleBirthAppointment)viewSingleAppointment;//get relevant properties and set to view}Just seems like i will end up using a very long select statement on multiple pages and not sure how to refactor?
Andi
+2  A: 

When you call .GetType(), you get the runtime type of the object. C# compiler doesn't know what runtime type your object will have. It only knows that your object is going to be of a type derived from tblAppointment because you said so in your method declaration, so the static type of the return value is tblAppointment. Therefore tblAppointment is all you can access, unless you use a cast to tell the compiler «I know that at runtime this reference is going to refer to an object of this type, insert a runtime check and give me a reference with this static type».

Static typing is all about the difference between types as known at compile time and as they are at runtime. If you come from a dynamically typed language like Smalltalk or Javascript, you'll have to make quite a few adjustments to your programming habits and thought processes. E.g., if you have to do something to an object that depends on its runtime type, the solution often is to use virtual functions — they dispatch on the object's runtime type.

Update: in your particular case, use virtual functions, this is exactly what they were made for:

class tblAppointment
{
    protected abstract void ProcessAppointment () ;
}

sealed class tblBirthAppointment
{
    protected override void ProcessAppointment ()
    {
        // `this` is guaranteed to be tblBirthAppointment
        // do whatever you need
    }
}

...

Then use

// will dispatch on runtime type
appointmentsRepo.GetAppointment (id).ProcessAppointment () ;
Anton Tykhyy
+6  A: 

You're solving the wrong problem. If you have a superclass A, with subclasses B, C, etc., that all have similar functionality, you want to do the following:

  1. Make A an interface that B, C, etc. implement. Code that works with B or C instances does by working through the interface provided by A. If you can define a common set of operations that work on all the types, then this is all you need to do.

  2. If you can't define a common set of operations, e.g. you have code similar to:

    A foo = GetA();
    if(foo is B) {
        B bFoo = (B) foo;
        // Do something with foo as a B
    } else if(foo is C) {
        C cFoo = (C) foo;
        // Do something with foo as a C
    } ...
    

    Or even this (which is basically the same thing, just using extra information to emulate what the type system already provides for you):

    A foo = GetA();
    MyEnum enumeratedValue = foo.GetEnumeratedValue();
    switch(enumeratedValue) {
        case MyEnum.B:
            B bFoo = (B) foo;
            // Do something with foo as a B
            break;
        case MyEnum.C:
            C cFoo = (C) foo;
            // Do something with foo as a C
            break;
    }
    

    Then what you really want is to do something like:

    A foo = GetA();
    foo.DoSomething();
    

    Where each subclass would implement the corresponding branch of the switch statement. This is actually better in several ways:

    • It uses less overall code.
    • Since the implementations of the cases live in the various implementation classes, no casting is necessary; they can access all the member variables directly.
    • Since you're not building a big switch/case block separate from the actual B and C implementations, you don't run any risk of accidentally forgetting to add a corresponding case if add a new subclass. If you leave the DoSomething() method out of a subclass of A, you will get a compile-time error.

Edit: In response to your comment:

If your DoSomething() routine needs to operate on a Form or other GUI element, just pass that element into the method. For example:

public class B : A {
    public void DoSomething(MyForm form) {
        form.MyLabel.Text = "I'm a B object!";
    }
}

public class C : A {
    public void DoSomething(MyForm form) {
        form.MyLabel.Text = "I'm a C object!";
    }
}

// elsewhere, in a method of MyForm:

A foo = GetA();
foo.DoSomething(this);

Alternatively, an even better idea might be to turn your B and C classes into custom controls, since they seem to encapsulate display logic.

Daniel Pryden
But how would this work if the switch statement at the moment sets labels and input values on the form? Sorry i know this is probably basic, but .NET is taking a little while longer to adjust to than i thought :)
Andi