C# 用于泛型类型层次结构的Fluent接口

C# 用于泛型类型层次结构的Fluent接口,c#,.net,generics,fluent,C#,.net,Generics,Fluent,我相信这是实现我想要的唯一方法,但我想把它放在那里,看看是否有一个不需要使用动态/反射的解决方案 我有以下类型的层次结构,剥离到最基本的部分来说明这一点: // Validators: public abstract class Validator<T> { } public class RequiredValidator<T> : Validator<T> { } // Fields: public abstract class Field { }

我相信这是实现我想要的唯一方法,但我想把它放在那里,看看是否有一个不需要使用动态/反射的解决方案

我有以下类型的层次结构,剥离到最基本的部分来说明这一点:

// 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>
我是否错过了一种避免使用基于
动态
的代码的方法?

C#中的泛型是全有还是全无。你要么全部通过,就像你所做的那样,要么一个也不通过。它的设计必须使所有参数都可以推断。对于您正在做的事情,您只需使用
字段
,而不是
TField
,删除该泛型类型参数;尽管它可能不那么理想。还有其他的方法。。。一些流畅的设计返回包含泛型的新类型作为属性,允许您继续,但您的延续也需要使用该延续类型的逻辑。这有点让人困惑,但我觉得你能理解。如果不让我知道


如果
where
约束也能帮助推断类型,那就太好了,但事实并非如此。Eric Lippert最近帮助我理解了C#仅尝试推断通用参数,如果无法推断,则会失败。
where
约束仅用于将泛型类型限制为一个基,并通知开发人员。虽然感觉我们也可以基于约束进行推断,但由于我们是基于类型,C#没有。埃里克对不这样做有自己的看法,我肯定这超出了我的理解。无论哪种方式,你都可以使用它。

对于“可扩展”的fluent接口,我们在Java中使用以下技巧(如果在C#中也有可能,你可以尝试):

公共类字段{
公共L必选(){
//...
返回(L)本文件;
}
}
公共类ValueField扩展字段{
}
现在,您可以调用您需要的:

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

这要归功于
字段
的附加类型参数,它将fluent方法的特定返回类型委托给孩子们。

我认为直接在
字段
上添加
所需的
方法(而不是使用扩展方法)这不是一个选项吗?@KevinGosse它当然是一个选项,但是对
ValueField
类型化变量的赋值将失败,因为它将返回一个
字段
,并打破总是返回实际操作字段类型的fluent调用链。C#中的泛型是全有或全无。你要么全部通过,就像你所做的那样,要么一个也不通过。它的设计必须使所有参数都可以推断。对于您正在做的事情,您只需使用
字段
,而不是
TField
,删除该泛型类型参数;尽管它可能不那么理想。还有其他的方法。。。一些流畅的设计返回包含泛型的新类型作为属性,允许您继续,但您的延续也需要使用该延续类型的逻辑。这有点让人困惑,但我觉得你能理解。如果不让我知道。如果
where
约束也能帮助推断类型,那就太好了,但它不能。Eric Lippert最近帮助我理解了C#仅尝试推断通用参数,如果无法推断,则会失败。
where
约束仅用于将泛型类型限制为一个基,并通知开发人员。虽然感觉我们也可以基于约束进行推断,但由于我们是基于类型,C#没有。埃里克对不这样做有自己的看法,我肯定这超出了我的理解。不管是哪种方式,你都有。是的,我理解问题所在-github/csharplang上有一个功能请求,要求使用
where
信息来帮助推断泛型类型,我只是想看看是否有人有一些静态/非动态黑客可以绕过它。在这个特定的示例中,动态
并不是什么大问题,只是很容易说明问题,但其他情况下性能更为关键,因此即使有一种迂回的静态方式,我也可能会这样做。谢谢,我以前使用过这种模式,但如果我没记错的话,它只适用于直接的子类,而不适用于更深层次。我正在使用的类型层次结构要复杂得多,它的局限性并不是真的指向子类,但你是对的,它仅限于类型层次结构中的叶子。您可以使用
ValueField扩展字段
SpecificValueField扩展值字段
。但您不能扩展
SpecificValueField
,仍然可以从这种模式中获益。
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();
}
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();