views:

116

answers:

7

Ok so I'm currently working with a set of classes that I don't have control over in some pretty generic functions using these objects. Instead of writing literally tens of functions that essentially do the same thing for each class I decided to use a generic function instead.

Now the classes I'm dealing with are a little weird in that the derived classes share many of the same properties but the base class that they are derived from doesn't. One such property example is .Parent which exists on a huge number of derived classes but not on the base class and it is this property that I need to use.

For ease of understanding I've created a small example as follows:

class StandardBaseClass {} // These are simulating the SMO objects

class StandardDerivedClass : StandardBaseClass {    
    public object Parent { get; set; }    
}

static class Extensions    
{    
        public static object GetParent(this StandardDerivedClass sdc) {
            return sdc.Parent;
        }

        public static object GetParent(this StandardBaseClass sbc)
        {
            throw new NotImplementedException("StandardBaseClass does not contain a property Parent");
        }

        // This is the Generic function I'm trying to write and need the Parent property.    
        public static void DoSomething<T>(T foo) where T : StandardBaseClass
        {
            object Parent = ((T)foo).GetParent();
        }

}

In the above example calling DoSomething() will throw the NotImplemented Exception in the base class's implementation of GetParent(), even though I'm forcing the cast to T which is a StandardDerivedClass.

This is contrary to other casting behaviour where by downcasting will force the use of the base class's implementation.

I see this behaviour as a bug. Has anyone else out there encountered this?

+1  A: 

Create an interface that contains the Parent property. Have each class that has a Parent property implement that interace. You will then be able to create a generic method that accepts a parameter of type IHaveParent, and it will do the right thing.

John Saunders
"I'm currently working with a set of classes that I don't have control over"
Foxfire
Yeah, I guess you can't do this if they're not partial classes.
John Saunders
Yeah, as I said that isn't an option. I'm working with SQL Server SMO objects that are typically sealed.
Adam Hardy
A: 

To me this is a divergence in the class hierarchy. By this this I mean that either the base class has parent, or the derived classes with Parent are derived from an abstract child of the base.

Lol as John says, an interface as opposed to an abstract class is sufficient too.

Preet Sangha
Which would be the obvious choice if I could change the classes I'm working with.
Adam Hardy
And the divergence you speak of is in Microsoft's SMO hierarchy, and I completely agree with you there.
Adam Hardy
+3  A: 

I see this behaviour as a bug.

This behavior is correct. Since your method DoSomething is constraining T to StandardBaseClass, you only have access to the specific methods of StandardBaseClass, not any methods or properties of a derived class. Since StandardBaseClass does not have a Parent property, this is invalid, and should be invalid, by design.

There are two potential options here - You can use reflection to pull out the Parent property, or use C# 4's dynamic type, and treat this as a dynamic object. Both bypass the standard type checking in the compiler, however, so will require you to do extra type checking at runtime to verify that the Parent property exists.

Reed Copsey
Hi Reed, I think from your explanation the penny dropped. It is by design that generic functions are constrained in this manner when specifying the constraining class. As opposed to specifying one or many interface\s. I just wish it wasn't! :)
Adam Hardy
A: 

You idea won't work because the compiler can never guarantee that the base class actually would have such a property. And it won't just select the "right" one based on if it has it or not.

The only way you can do this is using reflection and then test at runtime if the requested property exists on the inspected class. You have to judge yourself if that is a viable way to do for your project (reflection is slow and requires maximum rights).

Foxfire
I'm assuming you mean "the ?DERIVED? class actually would have such a property". And I see what you mean, it would be a little difficult for the compiler to then have to parse the generic function to ensure it would work with each an every derived class you called it with, not to mention confusing for the developer.
Adam Hardy
A: 

This is correct, as the compiler only knows that it can bind to your type as a StandardBaseClass. The binding is not done at runtime (where it could potentially decide to use the StandardDerivedClass overload.

If you know that it's a StandardDerivedClass, then why not just cast it as such?

object Parent = ((StandardDerivedClass)foo).Parent;
Adam Robinson
Well, because there are some 50+ objects with this property within the SMO Class hierarchy that I'm working with. I was trying to avoid some massive case statement which is what would result from your suggestion.
Adam Hardy
A: 

It's a bit ugly, but you can accomplish this using a Registration system, where you register delegates for different possible derived classes that expose the 'shared' property/method and then use something like a Dictionary<Type,Func<SomeT>> to store the delegates. If you know all of the derived types ahead of time and don't have to load plug-ins or the like, you can also use the classic ugly if/else-if structure. Either way you're basically creating your own substitute for what should have been supported by the virtual method table.

Dan Bryant
+1  A: 

For anyone that is interested an succinct answer to this situation is answered by Stephen Cleary on msdn here:

http://social.msdn.microsoft.com/Forums/en-AU/csharpgeneral/thread/95833bb3-fbe1-4ec9-8b04-3e05165e20f8?prof=required

Adam Hardy