views:

95

answers:

3

I was looking at the answer of this question regarding multiple generic types in one container and I can't really get it to work: the properties of the Metadata class are not visible, since the abstract class doesn't have them. Here is a slightly modified version of the code in the original question:

public abstract class Metadata
{
}

public class Metadata<T> : Metadata
{
    // Per Ben Voigt's comments, here are the rest of the properties:
    public NUM_PARAMS NumParams { get; set; }
    public FUNCTION_NAME Name { get; set; }
    public List<Type> ParamTypes { get; set; }
    public Type ReturnType { get; set; }
    //...C
    public T Function { get; set; }
    public Metadata(T function)
    {
        Function = function;
    }
}

List<Metadata> metadataObjects;
metadataObjects.Add(new Metadata<Func<double,double>>(SomeFunction));
metadataObjects.Add(new Metadata<Func<int,double>>(SomeOtherFunction));
metadataObjects.Add(new Metadata<Func<double,int>>(AnotherFunction));

foreach( Metadata md in metadataObjects)
{
      var tmp = md.Function; // <-- Error: does not contain a definition for Function
}

The exact error is:

error CS1061: 'Metadata' does not contain a definition for 'Function' and no extension method 'Function' accepting a first argument of type 'Metadata' could be found (are you missing a using directive or an assembly reference?)

I believe it's because the abstract class does not define the property Function, thus the whole effort is completely useless. Is there a way that we can get the properties?

Update

The basic idea is that I have a genetic program that uses the Metadata of functions (or MetaFunctions) in order to construct expression trees with those functions. The meta data allows me to correctly match the return from one function with the input parameters of another function... it basically turns my functions into legos and the computer can combine them in various ways. The functions are all within the same "domain", so I won't have any problem with randomly mixing and matching them.

I'm storing the Metadata, or MetaFunctions, into a couple of dictionaries:

  • one has the name of the function as the key.
  • the other has the number of parameters as the key.

In any case, I just tried to stick as close to the original question as possible... the fundamental problem is the same regardless if I use a List or a Dictionary. I'm also stuck with .NET 3.5 and I won't be able to update to .NET 4.0 for a while.

A: 

you are right, the error is because the list thinks it has a bunch of Metadata objects so when you iterate it you get back metadata references, in order to access a property defined in a subclass you need to make sure that the object actually is that subclass and then do the cast.

foreach( Metadata md in metadataObjects)
{
      var tmp =((Metadata<Func<double,double>>)md).Function; // but this will obviously fail if the type is incorrect. 
}

so here you are really just trading a definite compile time error for a potential run time error (depending on what is in your list). The real question is: What do you want to do with all these different function delegate wrappers? what do you expect the type of your tmp variable to be?

You could also try a type testing solution like this

foreach( Metadata md in metadataObjects)
{
    var dd_md = md as Metadata<Func<double,double>>;
    var id_md = md as Metadata<Func<int,double>>;
    var di_md = md as Metadata<Func<double,int>>;
    if(dd_md != null)
    {
       var tmp1 =dd_md.Function;
    }
    else if(id_md != null)
    {
       var tmp2 =id_md.Function;
    }
    else if(di_md != null)
    {
       var tmp3 =di_md.Function;
    }
    //etc....

}

this could also be a viable solution as long as you know exactly what types there will be ahead of time, but its annoying and error prone.

luke
@luke, I updated my question and added the rest of the properties in the Metadata. The basic idea is that I have a genetic program that uses the Metadata of functions in order to construct expression trees with those functions. The Metadata allows me to correctly match the return parameters from one function with the output parameters of another function... it basically turns my functions into legos and the computer can combine them in various ways.
Lirik
@luke, yes- doing the if/else is a bit ugly. I actually have enums that match the function names of each available function, so I could use a switch statement when I'm calling the functions... I'm also holding a dictionary with the function name and I'd like to just pass the function name as the key and get the metadata + the function.
Lirik
+2  A: 

What would you do with md.Function if you could read it? You can't call it, because you don't know the parameter types. With C# 4.0, you could use dynamic, e.g. foreach (dynamic md in metadataObjects) and then you don't need the Metadata abstract base class. If you just want to access members of Delegate, you could change the abstract base class to an interface which has a Delegate Metadata { get; } property and explicitly implement it in Metadata<T>, then you could access e.g. the function's name.

Ben Voigt
Ben Voigt, the parameter types are part of the Metadata... I'll update the code so you can see how it looks. This is part of a Genetic Program, so I'm providing the MetaData for all of the functions and I'm allowing the program to match functions based on the meta data.
Lirik
For this I think you don't need any generics at all. Simply expose the property as type Delegate, then you can actually query the parameter types as so forth from the Delegate and don't need to keep track of them yourself. And, it you have the right parameters, you can call Invoke to call the function.
Ben Voigt
@Ben, I need the generics because I'm doing memoization of the functions: http://stackoverflow.com/questions/2852161/c-memoization-of-functions-with-arbitrary-number-of-arguments
Lirik
@Ben, I don't understand what you mean when you say: "change the abstract base class to an interface which has a Delegate Metadata { get; } property and explicitly implement it in Metadata<T>"... specifically when you say it has "Delegate Metadata{ get; }", can you shoe me some sample code?
Lirik
I mean make the type of the property "Delegate", the base class for all delegate types... http://msdn.microsoft.com/en-us/library/system.delegate.aspx Pack your arguments into an array and call http://msdn.microsoft.com/en-us/library/system.delegate.dynamicinvoke(v=VS.100).aspx You can also query the parameter types, etc., by either retrieving the "Invoke" method or querying the Method property http://msdn.microsoft.com/en-us/library/system.delegate.method(v=VS.100).aspx
Ben Voigt
+1  A: 

I think the main problem here is that you are trying to solve a very Dynamic problem with the very Static (but flexible) tools of Generic Programming. So i see two ways for you to go.

  1. Split all your collections along type boundaries, creating a different collection for each type of function you have. This should be possible in your case because you know all the types ahead of time so you will know what types to create.
  2. Embrace the dynamic nature of the problem you are trying to solve and then use the right tools for the job. From what I can tell you want to be able to store a list of 'Functions' and then dynamically decide at run time which ones to call with which arguments. In this case you just need a better model.

I would go with option 2. From my understanding I think that this would be a better model.

public class Variable
{
    public Type Type {get; protected set;}
    public Object Value {get;protected set;}
    public Variable(Object val)
    {
        Type = val.GetType();
        Value = val;
    }
    public Variable(Type t, Object val)
    {
        Type = t;
        Value = val;
    }
}

public class ComposableFunction
{
    public NUM_PARAMS NumParams { get; protected set; }
    public FUNCTION_NAME Name { get; protected set; }

    //our function signature
    public List<Type> ParamTypes { get; protected set; }
    public Type ReturnType { get; protected set; }

    private Delegate Function { get; set; }
    public Metadata (Delegate function)
    {
        Function = function;
    }
    public bool CanCallWith(params Variable vars)
    {
        return CanCallWith(vars);
    }
    public bool CanCallWith(IEnumerable<Variable> vars)
    {
        using(var var_enum = vars.GetEnumerator())
        using(var sig_enum = ParamTypes.GetEnumerator())
        {
            bool more_vars = false;
            bool more_sig =false;
            while(   (more_sig = sig_enum.MoveNext()) 
                  && (more_vars = var_enum.MoveNext())
                  && sig_enum.Current.IsAssignableFrom(var_enum.Current.Type));
            if(more_sig || more_vars)
                return false;
        }
        return true;
    }

    public Variable Invoke(params Variable vars)
    {
        return Invoke(vars);
    }
    public Variable Invoke(IEnumerable<Variable> vars)
    {
        return new Variable(ReturnType, Function.DynamicInvoke(vars.Select(v => v.Value)));
    }
}

So now we have a nice model that should fulfill your requirements, and because it takes no generic type parameters you should be able to access all of its functionality when you iterate through a List<ComposableFunction> or whatever.

luke
@luke, that's probably very close to what I think will work for this project... it's a pretty complicated idea, but you gave me some good pointers here that would help me.
Lirik