Свободный интерфейс для общей иерархии типов - PullRequest
0 голосов
/ 08 декабря 2018

Я считаю, что это единственный способ достичь того, чего я хочу, но я хотел бы показать это, чтобы увидеть, есть ли решение, которое не требует использования динамического / отражения.

У меня есть следующая иерархиятипов, обнаженных до самого необходимого, чтобы продемонстрировать смысл:

// Validators:

public abstract class Validator<T> { }

public class RequiredValidator<T> : Validator<T> { }

// Fields:

public abstract class Field { }

public abstract class Field<T> : Field
{
    public void AddValidator(Validator<T> validator) => 
        Console.WriteLine($"Added validator {validator.GetType()}");
}

public sealed class ValueField<T> : Field<T> { }
public sealed class ComputedField<T> : Field<T> { }
...many other field types that inherit Field<T>

Это пример использования свободного интерфейса, которого я хочу достичь:

ValueField<string> field1 = new ValueField<string>().Required();

Метод Required()должен быть доступен на всех типах, которые наследуют Field<T>.

Вот что я придумал:

public static class Extensions
{
    public static TField Required<TField, T>(this TField field) where TField : Field<T>
    {
        field.AddValidator(new RequiredValidator<T>());
        return field;
    }

    public static TField DynamicRequired<TField>(this TField field) where TField : Field
    {
        DynamicAddRequiredValidator((dynamic)field);
        return field;
    }

    private static void DynamicAddRequiredValidator<T>(Field<T> field)
    {
        field.AddValidator(new RequiredValidator<T>());
    }
}

void Main()
{   
    // This is desired API usage but results in error:
    // The type arguments for method 'Extensions.Required<TField,T>(TField)' cannot be inferred from the usage.
    ValueField<string> field1 = new ValueField<string>().Required();

    // This works but the user shouldn't have to specify types like this, makes it very annoying to use:
    ValueField<string> field2 = new ValueField<string>().Required<ValueField<string>, string>();

    // This works but requires dynamic:
    ValueField<string> field3 = new ValueField<string>().DynamicRequired();
}

Мне не хватает способа достижения этого, который избегает использования dynamic на основе кода?

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

Для «расширяемого» беглого интерфейса мы используем следующий прием в Java (вы можете попробовать, если это возможно и в C #):

public class Field<L extends Field<L, V>, V> {
    public L required() {
        //...
        return (L) this;
    }
}

public class ValueField<V> extends Field<ValueField<V>, V> {
}

Теперь вы можете вызывать то, что вам нужно:

ValueField<String> v = new ValueField<String>().required();

Это благодаря дополнительному параметру типа Field, который делегирует конкретный тип возврата беглых методов дочерним элементам.

0 голосов
/ 09 декабря 2018

Обобщения в C # - это все или ничего.Вы либо проходите все, как сделали, либо ничего.Он должен быть разработан таким образом, чтобы все аргументы могли быть выведены.Для того, что вы делаете, вы можете просто использовать Field<T> вместо TField<T>, удалив этот параметр универсального типа;хотя это может быть не так идеально.Есть и другие способы ... Некоторые разработки FLUENT возвращают новые типы, которые содержат обобщенные значения в качестве свойств, позволяющих вам двигаться вперед, но вашему продолжению также потребуется логика использования этого типа продолжения.Это немного сбивает с толку, но я чувствую, что вы понимаете.Если нет, дайте мне знать.

Было бы неплохо, если бы ограничение where могло также помочь вывести типы, но это не так.Эрик Липперт недавно помог мне понять, что C # пытается вывести только общие параметры, и если это не может быть выведено, то это терпит неудачу.Ограничение where предназначено только для ограничения универсального типа базой и информирования разработчиков.Хотя кажется, что мы можем также сделать вывод, основываясь на ограничениях, так как мы основываем типы, C # этого не делает.Эрик считает, что не стоит этого делать, я уверен, что это больше, чем я понимаю, банкоматВ любом случае, у вас есть это.

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