简体   繁体   中英

Force calling the derived class implementation within a generic function in C#?

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?

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.

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.

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

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.

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).

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;

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM