5

Let's suppose I have the following task:

var task = _entityManager.UseRepositoryAsync(async (repo) => 
{
    IEnumerable<Entity> found = //... Get from repository

    return new 
    {
        Data = found.ToList()
    };
}

What is the type of task?

Actually, it turns out to be: System.Threading.Tasks.Task<'a>,

where 'a is anonymous type: { List<object> Data }

How can I explicitly state this type without using var?

I have tried Task<a'> task = ... or Task<object> task = ... but can't manage it to compile.

Why do I need to do this?

I have a method (UseApplicationCache<T>), that takes a Func<Task<T>> as a parameter.

I also have a variable cache that the user might set to true or false.

If true, the above said method should be called and my task should be passed as argument, if false, I should execute my task without giving it as an argument to the method.

My end result would be something like this:

Func<Task<?>> fetch = () => _entityManager.UseRepositoryAsync(async (repo) =>
{
     IEnumerable<Entity> found = //... Get from repository
     return new { Data = found.ToList() };
}

return await (cache ? UseApplicationCache(fetch) : fetch());
Matias Cicero
  • 25,439
  • 13
  • 82
  • 154
  • Hmmm.... But why do you wrapping `Enumerable` into anonymous class? Why don't just return it "as is"? Or, if real return value shoud contain not only that list - well, why don't create real type for it, not anonymous one? – Andrey Korneyev Oct 02 '15 at 14:58
  • @AndyKorneyev Actually, the anonymous type cointains more properties, I just specified the `Data` property for simplicity. I can create a specific type for it, but there's nothing as flexible as an anoymous type, right? I'm looking forward to keep using it – Matias Cicero Oct 02 '15 at 14:59
  • 1
    well, I've suspected something like this (as I've stated). So what about real type instead anonymous one - and get rid of this headache? ;) – Andrey Korneyev Oct 02 '15 at 15:00
  • @AndyKorneyev I'm saving that as my last option if what I'm trying to accomplish is not possible (I'd really prefer using anonymous types) – Matias Cicero Oct 02 '15 at 15:02
  • You cannot use an anonymous type in a declaration. You can use `var` and let type inference do it's wonders but you cannot use `var` with `Func` so I do not believe you will be able to use an anonymous type for this specific case. – Martin Liversage Oct 02 '15 at 15:03
  • 2
    @MatiCicero In fact, you can't access anonymous type outside the scope where it was created. See [MSDN](https://msdn.microsoft.com/en-us/library/bb397696.aspx) for details. – Andrey Korneyev Oct 02 '15 at 15:03
  • @AndyKorneyev I managed to find a way. Please tell me what do you think about it. See posted answer – Matias Cicero Oct 02 '15 at 16:15
  • @MartinLiversage I somehow managed to get it working while keeping my anonymous object. Please tell me your thoughts about it. See posted answer. Thank you! – Matias Cicero Oct 02 '15 at 16:19

3 Answers3

7

How can I explicitly state this type

You cannot. An anonymous type has no name, hence cannot be explicitly mentioned.

The anonymous type can be inferred if you create a generic helper method. In your case, you can do:

static Func<TAnon> InferAnonymousType<TAnon>(Func<TAnon> f)
{
  return f;
}

With that you can just do:

var fetch = InferAnonymousType(() => _entityManager.UseRepositoryAsync(async (repo) =>
{
     IEnumerable<Entity> found = //... Get from repository
     return new { Data = found.ToList() };
}
));

return await (cache ? UseApplicationCache(fetch) : fetch());

The "value" of TAnon will be automatically inferred.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
0

I managed to accomplish this without having to create a explicit type (as the comments suggested):

Func<Task<object>> fetch = () => _entityManager.UseRepositoryAsync(async (repo) =>
{
    IEnumerable<Entity> found = //... Get from repository
    //I cast anonymous type to object
    return (object) new { Data = found.ToList() };
}

return await (cache ? UseApplicationCache(fetch) : fetch());
Matias Cicero
  • 25,439
  • 13
  • 82
  • 154
  • How are you going to get the information back from the anonymous type? Only option I can think of is reflection. – Martin Liversage Oct 02 '15 at 16:28
  • @MartinLiversage I'm using use a `dynamic` type. I can get `Data` property just by doing `result.Data`. Of course, I'd have to sacrifice IntelliSense. – Matias Cicero Oct 02 '15 at 16:31
  • @MatiCicero well, basically it will work (assuming calee and caller both located in the same assembly, since you will fail to get properties of boxed anonymous type from another assembly even with dynamic type/reflection). Note: it will be slightly worse performancewise than using explicit type due to boxing and then dynamic type overhead. But I don't think it will be a real bottleneck. – Andrey Korneyev Oct 02 '15 at 19:33
0

You can you dynamic type to do this:

Func<Task<dynamic>> fetch = () => _entityManager.UseRepositoryAsync(async (repo) =>
{
   IEnumerable<Entity> found = //... Get from repository
   return new { Data = found.ToList() };
}
Vova
  • 1,356
  • 1
  • 12
  • 26
  • Note that the anonymous type has internal visibility so you cannot use this trick if the property access happens from code in another assembly. – Martin Liversage Oct 02 '15 at 16:30
  • 1
    Using `dynamic` instructs the compiler to emit code to perform late binding on the instance using reflection. In this case the underlying type is an anonymous type and this type is internal to the assembly where it is created. This means that the reflection will fail when performed from code in another assembly. This is commonly experienced in [ASP.NET MVC when you want to use an anonymous type as the model](http://stackoverflow.com/questions/5120317/dynamic-anonymous-type-in-razor-causes-runtimebinderexception). – Martin Liversage Oct 02 '15 at 18:32