tags:

views:

312

answers:

8

I am extending a new class by inheriting from RolesService. In RolesService I have a static methog that I would like to override in my newly derived class. When I make the call from my derived object it does not use the overridden static method it actually calls the base class method. Any ideas?

public class RolesService : IRolesService
    {
    public static bool IsUserInRole(string username, string rolename)
    {
        return Roles.IsUserInRole(username, rolename);
    }
}

public class MockRoleService : RolesService
{
    public new static bool IsUserInRole(string username, string rolename)
    {
        return true;
    }
}
+5  A: 

You can't override a static method. A static method can't be virtual, since it's not related to an instance of the class.

The "overriden" method in the derived class is actually a new method, unrelated to the one defined in the base class (hence the new keyword).

Thomas Levesque
+3  A: 

You cannot override a static method.

If you think about it, it doesn't really make sense; in order to have virtual dispatch you need an actual instance of an object to check against.

A static method also can't implement an interface; if this class is implementing an IRolesService interface then I would contend that the method should not be static at all. It's better design to have an instance method, so you can swap out your MockRoleService with a real service when you're ready.

Aaronaught
+5  A: 

You can't override a static method. You might find this an interesting read.

keyboardP
A: 

A static method represents logic pertinent to the type itself. Once you inherit from that type, the static methods of the parent type cannot be assumed to apply. What you can do is the following:

public class RolesService : IRolesService
{
    public static bool IsUserInRole(string username, string rolename)
    {
        return Roles.IsUserInRole(username, rolename);
    }

    // call this guy within the class
    protected virtual bool DoIsUserInRole(string username, string rolename) 
    {
        return IsUserInRole(username, rolename);
    }
}

public class MockRoleService : RolesService
{
    public new static bool IsUserInRole(string username, string rolename)
    {
        return true;
    }

    protected override bool DoIsUserInRole(string username, string rolename)
    {
        return IsUserInRole(username, rolename); // invokes MockRoleService.IsUserInRole

        // or simply
        return true;
    }
}
gWiz
The call to IsUserInRole in the last method is ambiguous, you need to call MockRoleService.IsUserInRole
Thomas Levesque
@Thomas not sure if you saw the code before or after my edit, but in the current sample, the call to IsUserInRole invokes MockRoleService.IsUserInRole.
gWiz
A: 

There are two ways you might make your mock object work:

1: Change the signature of your parent object from RolesService to IRolesService (assuming you aren't using the Interface already) . Then implement the mock to IRolesService instead of RolesService.

public class MockRoleService : IRolesService
{
    public new static bool IsUserInRole(string username, string rolename)
    {
        return true;
    }
}
  1. Dependency inject the Roles object.

public class MockRoleService : RolesService {

public MockRoleService ()
{
     Roles = OverridenRolesObjectThatAlwaysReturnsTrue;
}

}

Jason Hernandez
+1  A: 

In order to call a static method, you'll need a direct reference of the type:

RolesService.IsUserInRole(...);

In which case, if you want to be able and call the "derived" class's static method, dropping the "new" keyword will allow you to:

MockRoleService.IsUserInRole(...);

and get the expected function call.

My guess is that this isn't what you're looking for. You likely have some call somewhere in your code like the former, and you're hoping that by using a Mocking tool to create a MockRoleService, you'd be injecting this new "type" in place of the old one. Unfortunately, that's not how it works with statics.

A mocking tool will create an instance of the mocked out type and will inject that in place of a call to construct the real type. A call to a static method skips all of that.

As Aaronaught mentioned, you should probably make this method a normal instance method. This would allow your mock service to be properly injected in place of your regular RolesService, allow you to move the method declaration into your IRolesService interface, and override it in your MockRoleService implementation. Then, in the code where you "get" a RolesService, you just call the instance member instead of the static.

IRolesService svc = MyServiceInjector.GetService<IRolesService>();
svc.IsUserInRole(...);
Jeff Wight
+2  A: 

Doing the following the will allow you to work around the static call. Where you want to use the code take an IRolesService via dependency injection then when you need MockRolesService you can pass that in.

public interface IRolesService
{
    bool IsUserInRole(string username, string rolename);
}

public class RolesService : IRolesService
{
    public bool IsUserInRole(string username, string rolename)
    {
        return Roles.IsUserInRole(username, rolename);
    }
}

public class MockRoleService : IRolesService
{
    public bool IsUserInRole(string username, string rolename)
    {
        return true;
    }
}
MHinton
A: 

What about something like this:

public interface IRolesService
{
    bool IsUserInRoleImpl(string username, string rolename);
}

public abstract class RolesServiceBase<T>  where T : IRolesService, new()
{
    protected static T rolesService = new T();

    public static bool IsUserInRole(string username, string rolename)
    {
        return rolesService.IsUserInRoleImpl(username, rolename);
    }
}

public class RolesService : RolesServiceBase<RolesService>, IRolesService
{
    public bool IsUserInRoleImpl(string username, string rolename)
    {
        return Roles.IsUserInRole(username, rolename);
    }
}

public class MockRoleService : RolesServiceBase<MockRoleService>, IRolesService
{
    public bool IsUserInRoleImpl(string username, string rolename)
    {
        return true;
    }
}
fre0n