Частичное определение типа - PullRequest
12 голосов
/ 23 октября 2010

У меня есть такой общий метод (упрощенная версия):

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 ]

18 голосов
/ 23 октября 2010

Вы можете разделить t на универсальный метод для универсального типа:

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

Здесь вывод int, но строка явная.

3 голосов
/ 23 октября 2010

То, что вы пытаетесь достичь, невозможно. Вам необходимо указать оба общих аргумента или ни один из них, если вывод возможен.

0 голосов
/ 25 октября 2018

Вы можете использовать отражение ... как показано ниже

Вот пример того, как вызвать метод расширения с двумя общими параметрами.

У нас есть способы выполнить метод расширения:

а) Прямо из абстрактного базового класса б) из экземпляра объекта, который получен из этого базового класса

Не обязательно так реализовывать, но я нашел это очень удобным.

a) Вы должны предоставить два общих аргумента как обычно. б) у вас уже есть один из универсальных типов, так как вы используете экземпляр. Другой универсальный параметр должен передаваться как аргумент типа, вы не можете передать ему второй универсальный параметр из-за неоднозначности.

(см. Как передать 2 обобщенных типа в метод расширения )

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);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...