Question

I'm working on a Service locator system where services could register and get requested around the codebase.

The whole code if you're interested.

The problem that I'm having, I'm trying to make it so that if a service instance has already registered, registration fails, I'm doing it like so:

/// <summary>
/// Registers a service.
/// More than one instance of a service type is allowed.
/// Registration will fail if the same instance has already registered itself.
/// </summary>
public static void Register(IService service)
{
    Type type = service.GetType();
    List<IService> list = GetServiceList(type, out list);

    if (list.IsEmpty())
        dic[type] = list;
    else if (list.Contains(service))
        throw new RegistrationException("[Vervices]: Service instance: `" + service + "` has already registered!");
    else if (list.FirstOrDefault(s => s.Identifier == service.Identifier) != null)
        throw new RegistrationException("[Vervices]: There already exist a service instance of id: `" + service.Identifier);

    list.Add(service);

    if (service is MonoBehaviour)
        Object.DontDestroyOnLoad(service as MonoBehaviour);
}

Instead of doing a list.Contains(service) I thought why not let each service has a HasRegistered - and when I register a service, I set that to true. Now the interface will look like:

public interface IService
{
    void Ping(Object sender);
    string Identifier { get; }
    bool HasRegistered { get; set; }
}

Now I could just do if (service.HasRegistered) throw exception; instead of if (list.Contains(service) throw exception;

But the problem is, that is not secure. The property has both a public setter and getter which means any outsider could come in and do service.HasRegistered = false;!!!

It should be set to true, ONLY inside Register - How can I do that? - If I make the setter private, I can't set it anywhere, if I make a NotifyHasBeenRegistered() inside IService same problem, an outsider could call it and cause problems.

How can I do what I want, in a secure way?

Thanks for your help.

Was it helpful?

Solution

Personally, I'd stick with the Contains approach, for two reasons:

  • The Locator should be responsible for knowing which services have been registered - not the services (Single Responsibility Principle).
  • The only way to make the service unable to set the property, is to use inheritance with internal properties which only the locator can set. Again, forcing the service to inherit your base class is very restrictive (since c# doesn't support multiple inheritance).

If you're worried about the Contains performance, you could use a data structure with better performance than List<T>. "Skip lists" are a kind of list with properties similar to Binary Search Trees. It allows you to search for a specific item in O(log n) time, whereas List<T> takes O(n) time (i.e., much slower).

NGenerics provides implementations for Skip lists and several other useful data structures: https://github.com/ngenerics/ngenerics

Here's a cheat sheet for data structures performance: http://bigocheatsheet.com/#data-structures

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top