部分类型推断

时间:2010-10-23 10:37:40

标签: c# generics

我有这样的通用方法(简化版):

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

在上文中,param有意为object类型。这是要求的一部分。

当我填写类型时,我可以这样称呼它:

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

但是,我想使用类型推断。最好,我想写一下:

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

但是这不能编译。我想出的最好的是:

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

我希望将此作为类型参数并且仍具有正确类型的返回类型的原因是因为我的实际调用看起来像这样:

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

哪个非常难看,我很乐意写这样的东西:

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

3 个答案:

答案 0 :(得分:18)

您可以将t拆分为泛型类型的泛型方法:

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

这里推断出int,但字符串是显式的。

答案 1 :(得分:3)

你想要实现的目标是不可能的。如果可以进行推理,则需要指定两个泛型参数,或者不指定它们。

答案 2 :(得分:0)

您可以像下面这样使用反射...

这是一个如何使用两个通用参数调用扩展方法的示例。

我们必须执行扩展方法的方法:

a)直接来自抽象基类 b)从该基类派生的实例对象中

不是强制实现这样的,但是我发现它非常方便。

a)您必须照常提供两个通用参数。 b)由于您正在使用实例,因此您已经具有一种通用类型。另一个通用参数必须作为类型实参传递,由于模棱两可,您不能将第二个通用参数传递给它。

(请参阅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);
    }