7

I have a singleton that can register a func to resolve an id value for each type:

public void RegisterType<T>(Func<T, uint> func)

for example:

RegisterType<Post>(p => p.PostId );
RegisterType<Comment>(p => p.CommentId );

and then i want to resolve the id for an object, like these:

GetObjectId(myPost);

where GetObjectId definition is

public uint GetObjectId(object obj)

The question is, how can i store a reference for each func to invoke it lately. The problem is that each func has a different T type, and I can't do something like this:

private Dictionary<Type, Func<object, uint>> _typeMap;

How can resolve it? Expression trees?

regards Ezequiel

eze1981
  • 780
  • 1
  • 7
  • 19

6 Answers6

2

You have two options:

  1. Change GetObjectId to a generic function that takes a T.
    You can then store the Func<T, uint>s in a generic static class and call them by writing FuncStorage<T>.Value(obj).

  2. Use expression trees to Create Func<object, uint>s that calls the Func<T, uint> (using a cast) and put those in your Dictionary<Type, Func<object, uint>>.
    EDIT: You don't need expression trees to do that; you can use a normal lambda expression which casts to T. I was thinking of the reverse case (generating a generic delegate from a non-generic one), which does require expression trees.

Community
  • 1
  • 1
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Thanks SLaks, i cant change GetObjectId to GetObjectId because i don´t known the type on runtime. – eze1981 Feb 28 '11 at 15:18
2

You don't need Expression Trees to do it the way you are suggesting, just need to nest the function when registering it.

public void RegisterType<T>(Func<T, uint> func){
             _typeMap.Add(typeof(T), obj=>func((T)obj));
}
jbtule
  • 31,383
  • 12
  • 95
  • 128
1
public class Registration
    {
        public static Registration Instance = new Registration();

        private Registration()
        {
        }

        private Dictionary<Type, object> Dictionary = new Dictionary<Type, object>();

        public void Register<T>(Func<T, uint> aFunc)
        {
            Dictionary[typeof(T)] = aFunc;
        }

        public uint GetId<T>(T aT)
        {
            var f = Dictionary[typeof(T)];
            var g = (Delegate)f;
            return (uint) g.DynamicInvoke(aT);
        }
    }
Pedro
  • 382
  • 2
  • 7
  • `DynamicInvoke` is slow but in @Pedro's solution it's not necessary. The type actually can be known in this example such that `var g = (Func)f;` then `return g(aT)`. While this may not work for @eze1981 it could work for others. – jbtule Mar 01 '11 at 14:39
1

The problem is that each func has a diferent T type, and i cant do something like these:

private Dictionary<Type, Func<object, uint>> _typeMap;

I'm not sure why you say this. This works:

class Post
{
    public uint PostId { get; set; }
}

static public void RegisterType<T>(Func<T, uint> getUintFromT)
{
    Func<object, T> toT = (t => (T)t);

    Func<object, uint> getUintFromObject = 
        @object => getUintFromT(toT(@object));

    _typeMap.Add(typeof(T), getUintFromObject);
}

static public uint GetObjectId(object obj)
{
    return _typeMap[obj.GetType()](obj);
}

Usage:

class Program
{
    private static Dictionary<Type, Func<object, uint>> _typeMap 
        = new Dictionary<Type, Func<object, uint>>();

    static void Main(string[] args)
    {
        RegisterType<Post>(p => p.PostId);

        Post myPost = new Post();
        myPost.PostId = 4;

        var i = GetObjectId(myPost);

        Console.WriteLine(i);
        Console.ReadKey();

    }
}
AakashM
  • 62,551
  • 17
  • 151
  • 186
1

@SLacks, following your advise i have changed my approach to:

private Dictionary<Type, Func<object, uint>> _typeMap;

public void RegisterType<T>(uint typeId, Func<T, uint> func)
{            
    _typeMap[typeof(T)] = (o) => func((T)o);
}

public uint GetObjectId(object obj)
{
    return _typeMap[obj.GetType()](obj);
}

thanks!

eze1981
  • 780
  • 1
  • 7
  • 19
0

I cant change GetObjectId to GetObjectId<T> because i don´t known the type on runtime.

So, i have changed the dictionary definition to:

private Dictionary<Type, Delegate> _typeMap 

and then invoke it via Reflection:

public uint GetObjectId(object obj)
{
    uint id = 0;

    var objType = obj.GetType();

    // busco si el type está registrado
    if (_typeMap.Keys.Contains(objType))            
    {
        id = (uint) _typeMap[objType].Method.Invoke(obj, new object[] { obj } );                
    }

    return id;
}

Thank you all so much

eze1981
  • 780
  • 1
  • 7
  • 19
  • Look at my answer. You can call GetId without knowing the type at runtime. It will be inferred. – Pedro Feb 28 '11 at 15:36
  • 1
    You should make the dictionary hold `Func`, by making a lambda that casts the object parameter to `T`. This will be much faster. – SLaks Feb 28 '11 at 18:31
  • 1
    @Pedro: Wrong. Type inference can only happen at compile-time. – SLaks Feb 28 '11 at 18:32
  • @SLaks Yes, the inference occurs at compile time. But look at my code. It will work. – Pedro Feb 28 '11 at 19:43
  • @Pedro the inference at compile time will be `GetId` thus when `Dictionary[typeof(object)]` won't be able to find anything. Actually since you used `DynamicInvoke` you didn't need to use generics at all on GetId thus `Dictionary[aT.GetType())]` would have made it work, though it would be slow, similarly slow to @eze1981 solution here since `DynamicInvoke` is just a short cut for `Method.Invoke`. – jbtule Mar 01 '11 at 14:51