14

I have a generic method like this (simplified version):

public static TResult PartialInference<T, TResult>(Func<T, TResult> action, object param)
{
    return action((T)param);
}

In the above, param is of type object on purpose. This is part of the requirement.

When I fill in the types, I can call it like this:

var test1 = PartialInference<string, bool>(
    p => p.EndsWith("!"), "Hello world!"
);

However, I'd like to use type inference. Preferably, I would like to write this:

var test2 = PartialInference<string>(
    p => p.EndsWith("!"), "Hello world!"
);

But this does not compile. The best I came up with is this:

var test3 = PartialInference(
    (string p) => p.EndsWith("!"), "Hello world!"
);

The reason I would like to have this as a type parameter and still have the correctly typed return type is because my actual calls look something like this:

var list1 = ComponentProvider.Perform(
    (ITruckSchedule_StaffRepository p) => p.GetAllForTruckSchedule(this)
)

Which is very ugly and I would love to write as something like this:

var list2 = ComponentProvider.Perform<ITruckSchedule_StaffRepository>(
    p => p.GetAllForTruckSchedule(this)
)
Pieter van Ginkel
  • 29,160
  • 8
  • 71
  • 111

3 Answers3

21

You can split t into a generic method on a generic type:

class Foo<TOuter> {
    public static void Bar<TInner>(TInner arg) {...}
}
...
int x = 1;
Foo<string>.Bar(x);

Here the int is inferred but the string is explicit.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
3

What you are trying to achieve is not possible. You need to specify both generic arguments or none of the them if inference is possible.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Any ideas for a rewrite of the method that would still get rid of the ugly typing of the `p` parameter? – Pieter van Ginkel Oct 23 '10 at 10:45
  • I think the last version you had is not as bad: you just need an additional generic parameter for the return type. – Darin Dimitrov Oct 23 '10 at 10:48
  • That's the whole point. The type is there, the `GetAllForTruckSchedule` has a return type, and I do not want to specify this. This method is used e.g. in Linq queries and I would like to have inference do it's work as much as possible. `var` all the way :). – Pieter van Ginkel Oct 23 '10 at 10:52
0

You can use reflection... like this below

Here is an example of how to call a extension method with two generic parameters.

We have to ways to execute the extension method:

a) Directly from an abstract base class b) From an instance object that derived from that base class

Not mandatory to implement like so, but I found it very handy.

a) You must supply the two generic arguments as usual. b) You already have one of the generic types since you are using an instance. The other generic parameter must by passed as type argument, you cannot pass it a second generic parameter due to ambiguity.

(see How to pass 2 generics types into an extension method)

public interface IEntityDto
{
    // Not relevant to this example, how is defined , is just an interface, it could be removed, if your generic types don't need interface constraints
}

public interface IRowVersion
{
    // Not relevant to this example, how is defined , is just an interface, it could be removed, if your generic types don't need interface constraints
}

public interface IPropertyMappingValue
{
    // Not relevant to this example, how is defined , is just an interface, it could be removed, if your returned object don't need interface constraints
    string Value { get; set; }
}

public class PropertyMappingValue : IPropertyMappingValue
{
    // Not relevant to this example, how is defined , is just an object, returned by our extension method
    public string Value { get; set; }
}

public abstract class EntityBase
{
    public static IPropertyMappingValue GetPropertyMappingValue<TEntity, TEntityDto>(string name) where TEntity : class, IRowVersion where TEntityDto : class, IEntityDto => EntityExtensions.GetPropertyMappingValue<TEntity, TEntityDto>(name);
}

// Sample Class
public class Entity : IRowVersion
{
}

// Sample Class
public class EntityDto : EntityBase, IEntityDto
{
}

public static class EntityExtensions
{
    public static IPropertyMappingValue GetPropertyMappingValue<TEntityDto>(this TEntityDto instance, Type entityType, string name) where TEntityDto : class, IEntityDto
    {

        if (!typeof(IRowVersion).IsAssignableFrom(entityType))
            throw new ArgumentException($"{entityType} do not implements {typeof(IRowVersion)}");

        var method = typeof(EntityExtensions).GetMethod(nameof(GetPropertyMappingValue), new[] { typeof(string) });
        var typeArgs = new[] { entityType, typeof(TEntityDto) };
        var constructed = method?.MakeGenericMethod(typeArgs);

        var result = constructed?.Invoke(null, new object[] { name });

        return result as IPropertyMappingValue;
    }

    public static IPropertyMappingValue GetPropertyMappingValue<TEntity, TEntityDto>(string name) where TEntity : class, IRowVersion where TEntityDto : class, IEntityDto
    {
        //TO DO YOUR JOB HERE TO GET A VALID RETURNED OBJECT, as this is an example I will return a fake
        // THE CODE IS JUST AN EXAMPLE of doing something with the types, but is not relevant for this example
        //
        var foo = typeof(TEntityDto);
        var bar = typeof(TEntity);
        //

        return new PropertyMappingValue { Value = name }; // returning just a fake object
    }
}

public class UnitTest
{
    private readonly ITestOutputHelper _console;

    public UnitTest(ITestOutputHelper console)
    {
        _console = console;
    }

    [Fact]
    public void Test()
    {
        var oneWayOfExecuting = EntityBase.GetPropertyMappingValue<Entity, EntityDto>("Hello world"); //using a abstract base 

        _console.WriteLine(oneWayOfExecuting.Value);

        var entityDto = new EntityDto();
        var anotherWayOfExecuting = entityDto.GetPropertyMappingValue(typeof(Entity), "Hello world"); //using the extension method
        _console.WriteLine(anotherWayOfExecuting.Value);

        Assert.Equal("Hello world", oneWayOfExecuting.Value);

        Assert.Equal("Hello world", oneWayOfExecuting.Value);
    }
alhpe
  • 1,424
  • 18
  • 24