tags:

views:

81

answers:

7

Is it possible to obtain a reference/pointer to a class type and enforce that it derives from a particular base class?

I'm writing a client library that needs to negotiate with a server to pick an algorithm to use for communication. I want the user of the library to be able to select a subset of algorithms to use and not be fixed to the set I initially provide (ie. not fixed in some kind of factory class).

Ideally this would be done by passing in a list of classes that derive from some common "Algorithm" subtype. I've seen the "Type" object but I would have to check all the types myself. Is there a way to have the compiler do this for me? What I want is something like "Type<Algorithm>" but I can't find anything like that. Or is there different way entirely to do this?

An example of what I've thought of so far:

public class Algorithm {
    public static abstract Name;
}

public class Client {
    public MyLib(Type[] algorithms) {
      m_algorithms = algorithms;
      // ... Check they all derive from Algorithm
    }

    public Communicate() {
      // ... Send list of algorithm names to server
      // ... Create instance of algorithm dictated by server response
    }
}
A: 
public class Client<T> where T : Algorithm 
{
   ...
}

You have access to T, and can use typeof(T) if you need the actual Type. No need to pass anything into the constructor. However, this will only work with one algorithm, so probably does not answer your question. (You could add more type parameters, but it would be fixed and not open ended like an array)

Kirk Woll
+1  A: 
  1. Create an interface, IAlgorithm, that defines the minimum method definitions an algorithm requires by your app.
  2. Implement the IAlgorithm interface at least once.
  3. Restrict yourself to passing ONLY the IAlgorithm interface between your methods.

By doing this you can expose your IAlgorithm interface to potential integration developers to implement your interface for what they need and still have it work with your server. (Security of such a setup and whither this is a good idea or not is another discussion : )

Tahbaza
A: 

The Type object has a property BaseType, which is the type from which the class immediately derives. You could check if the type's BaseType is of type Algorithm, recursively looking for all ancestor types.

   bool IsDerivedFrom(Type typeToCheck, Type derivedFrom)
   {
      while (typeToCheck.BaseType != null)
      {
         if (typeToCheck.BaseType == derivedFrom) return true;
         typeToCheck = typeToCheck.BaseType;
      }
      return false;
   }

NOTE: As pointed out in a comment, use typeToCheck.IsSubclassOf(derivedFrom) does the same trick.

ILoveFortran
Or just call Type.IsSubclassOf() instead. It walks the inheritance chain.
dthorpe
I had realised this but I was hoping the compiler would do it for me
David Roe
+3  A: 

Is there a reason that you don't want to instantiate the Algorithm objects until Communicate() gets called?

If you were happy to pass in a list of instances, then you could do this:

public class Client {
    public MyLib(IList<Algorithm> algorithms) {
      m_algorithms = algorithms;
      // ... They all derive from Algorithm
    }
    public Communicate() {
      // ... Send list of algorithm names to server
      // ... Use instance of algorithm dictated by server response
    }
}

This would also allow you to write Algorithm implementations with tuning parameters, like this:

public class MyAlgorithm : Algorithm {
  public MyAlgorithm(int tolerance) {
    // ...
  }
}

and Communicate won't have to worry about how to construct the MyAlgorithm.

Douglas
Each algorithm may have a lot of state and require time consuming initialisation. The answer from StriplingWarrior seems like the best fit for what I want to do.
David Roe
A: 

You've got two options use Interfaces to define the basic contractual behaviour that you want in place of any your types and have each type of connection or algorithm that you define for your library implement this Interface.

Or define an abstract base type and then use generics to constraint type arguements passed into a connection or algortihm chooser method. So you could have a simple combo box on your U.I, that allows your user to select the alogrithm that they want to use, in order for one method to service all your needs for this very contextual scenario, come up with a simple method that meets your needs then 'GENERIC-ISE' it ... thats never gonna catch on lol.

public void algPicker(T t) where T: <base class name>
{

}

or you could use an interface.

public void algPicker(T t) where T: <interface name>
{

}

Above is what they call applying generic constraints to type arguements.

or ... forget generics.

public void algPicker(MyInterface type)
{

}

Rather than implementing a generic set-up, just use interfaces and decouple your method from being specific to certain type but to a group of types all of which implement the same interface. This way you will know only a certain group of types can be passed into your algorithm picker method as they all need to implement the same Interface inorder for them to be valid parameters for the method defined above.

In order to understand the above solutions, you will need to have read up on generics, abstraction and interfaces.

Happy reading and happy coding.

IbrarMumtaz
A: 

You unfortunately can't check that the types are all Algorithms at compile time. This is one of the (few) features I miss about Java. However, there are some good workarounds, which (depending on your situation) may be better than the solution you were hoping for. For example:

public abstract class Algorithm {
}

public class AlgorithmA : Algorithm { }
public class AlgorithmB : Algorithm { }

public interface IAlgorithmFactory
{
    string Name {get;}
    Algorithm GetAlgorithm();
}

public class AlgorithmFactory<T> : IAlgorithmFactory where T : Algorithm, new()
{
    public string Name {get { return typeof(T).Name; }}
    public Algorithm GetAlgorithm()
    {
        return new T();
    }
}

public class Client {
    public void MyLib(IEnumerable<IAlgorithmFactory> algorithms) {

    // ... Check they all derive from Algorithm
    }

    public void Communicate() {
    // ... Send list of algorithm names to server
    // ... Create instance of algorithm dictated by server response
    }
}

Then you can use your client class like this:

new Client().MyLib(new IAlgorithmFactory[] 
                   {
                       new AlgorithmFactory<AlgorithmA>(), 
                       new AlgorithmFactory<AlgorithmB>()
                   });
StriplingWarrior
This looks great. Thanks
David Roe
I just realised as well that if a certain Algorithm needs some kind of initialisation state then I can create a specific factory for it - boiler plate only when needed, excellent.
David Roe
Precisely. I followed your lead with the `MyLib` thing because I didn't know exactly what you were doing, but I'd suggest using constructor injection. That way you could do some really nice things with an IoC framework.
StriplingWarrior
+1  A: 

One approach would be to let / require the user of your library to pass instances of algorithms instead of types. Your list of available algorithms is then simply a List and the compiler will enforce the type requirement. Your library would not be responsible for constructing instances of algorithms, just using the provided algorithm instances. (This would also work if the instances provided by your user were factories rather than the actual algorithms.)

Another approach would be to use MEF to allow the library user to aggregate the algorithms they want to use without your direct involvement at all. You set up your library as a consumer of Algorithm, and use MEF to enumerate the available providers of Algorithm at runtime. The library user sets up their app to include N number of classes implementing Algorithm, and MEF will draw them all together and present them to you with a bow on top.

dthorpe
There will be some state attached to an algorithm so I don't want to create the ones that won't get used. MEF looks interesting though, I like the idea of it just working based on the imports the user makes. I'll have to look into that in more detail. Thanks.
David Roe