C# fluentapi的类型推断

C# fluentapi的类型推断,c#,generics,fluent,fluent-interface,C#,Generics,Fluent,Fluent Interface,我有以下扩展方法: public static IFoo Foo(this IFluentApi api, Action action); public static IFoo<TResult> Foo<TResult>( this IFluentApi api, Func<TResult> func); public static IBar Bar(this IFoo foo); public static void FooBar(this I

我有以下扩展方法:

public static IFoo Foo(this IFluentApi api, Action action);

public static IFoo<TResult> Foo<TResult>(
    this IFluentApi api, Func<TResult> func);

public static IBar Bar(this IFoo foo);

public static void FooBar(this IBar bar, Action action);

public static void FooBar<TResult>( // <- this one cannot work as desired 
    this IBar bar, Action<TResult> action);
我还需要实现以下扩展方法:

public static IBar<TResult> Bar<TResult> (this IFoo<TResult> foo);
publicstaticibar条(这个ifoofoo);
并将上述最后一种扩展方法更改为:

public static void FooBar<TResult>(
    this IBar<TResult> bar, Action<TResult> action);
publicstaticvoidfoobar(
此IBar条,动作);
由于我实际上不仅在
Foo()
FooBar()
之间有
Bar()
,而且还有非常长的方法链,因此我将有巨大的额外实现成本

是否有任何方法可以避免此问题并“神奇地”转发
TResult
通用参数

编辑:


不丢失类型推断

假设您能够从
IFoo
转换为
IFoo
,并且您的方法链不关心
TResult
,您可以通过将用法更改为以下内容来保存一些实现:

api.Foo(x => ReturnLong())
   .Bars(foo=>foo.Bar1() //where foo is an IFoo
                 .Bar2()
                 .Bar3()
                 ...
    )
   .FooBar(x => ...);

C#中的流畅接口依赖于通过每个
传递的类型(显式或隐式)。正如您所描述的,如果您丢失了类型信息,则无法将其取回

您唯一的选择是在表达式中具有分支,如Shawn的回答中所述,或者您必须仅具有
IBar栏(此IFoo foo)
,以便始终传递所需的类型信息

  • 当然,如果你的一些
    .Bar
    实际上像
    .First
    .SingleOrDefault
    一样,那么它们后面无论如何都不应该跟
    .FooBar
    (至少不直接跟在后面)

注意泛型类型必须在编译时已知。可以在运行时存储的实例,但不能使用它代替泛型参数

您已经创建了两种类型:
IFoo
IFoo:IFoo
。但是,
IBar
类是由
IFoo
创建的,它没有关于其类型的信息,因为它没有任何宿主。因此,类型信息丢失。我们只能考虑能够在编译时推断类型的解决方案。


第一个解决方案是已知的——创建调用链中使用的所有类型的通用版本。这需要很多努力


如果可以假设类型在执行期间不会更改,则可以包装该类型并显式使用方法:

api.Foo(() => default(long))
   .Bar()
   .FooBar<long>(x => { });

通过反射,您可以获得有关x的所有必需信息。

摆脱没有类型参数的IFoo接口,并将类型参数添加到IBar以从IFoo中记住类型。否则,不正确的程序将进行类型检查

public interface IFluentApi {}

public interface IFoo<T> {}

public interface IBar<T> {}

public struct Unit {}

public static class Extenders
{
    public static IFoo<Unit> Foo(this IFluentApi api, Action action) {return null;}

    public static IFoo<T> Foo<T>(this IFluentApi api, Func<T> func) {return null;}

    public static IBar<T> Bar<T>(this IFoo<T> foo) {return null;}

    public static void FooBar<T>(this IBar<T> bar, Action action) {}

    public static void FooBar<T>(this IBar<T> bar, Action<T> action) {}

    public static void CheckType<T>(this T value) {}
}

public class Examples
{
    public void Example()
    {

        IFluentApi api = null;

        api.Foo(() => "Value")
           .Bar()
           .FooBar(x => x.CheckType<string>()); // x is a string


        api.Foo(() => {})
           .Bar()
           .FooBar(x => x.CheckType<Unit>() ); // x is a Unit

        // The following (correctly) fails to type check
        Action<string> stringAction = Console.WriteLine;
        api.Foo(() => (long) 7)
           .Bar()
           .FooBar(stringAction); // x should be of type long
    }
}
公共接口IFluentApi{}
公共接口IFoo{}
公共接口IBar{}
公共结构单元{}
公共静态类扩展器
{
公共静态IFoo Foo(此IFluentApi,Action Action){return null;}
公共静态IFoo Foo(此IFluentApi,Func Func){return null;}
公共静态IBar条(此IFoo foo){return null;}
公共静态void FooBar(此IBar条,动作动作){}
公共静态void FooBar(此IBar条,动作动作){}
公共静态无效检查类型(此T值){}
}
公开课示例
{
公共无效示例()
{
IFluentApi=null;
api.Foo(()=>“值”)
.Bar()
.FooBar(x=>x.CheckType());//x是一个字符串
api.Foo(()=>{})
.Bar()
.FooBar(x=>x.CheckType());//x是一个单位
//以下内容(正确)未能进行类型检查
Action stringAction=Console.WriteLine;
api.Foo(()=>(长)7)
.Bar()
.FooBar(stringAction);//x的类型应为long
}
}

在您的fluent API中是否有任何理由使用非泛型方法?如果没有,就完全抛弃它们,转而使用通用的。为什么没有一个
公共静态IBar条(这个ifoofoo)?@Chris:很遗憾,是的,我应该在Foo(操作)中返回什么?@supercat:用户不需要,那么为什么要实现它呢?不幸的是,如果我想使用类型推断,我必须实现它……现在的问题是:如何克服这个缺陷?我根本不会把它称为“缺陷”。我认为最好的选择是实现所有方法,或者创建一个包装泛型生成器,其中在类级别定义一次
TResult
。(我想这样做意味着放弃扩展方法的使用,仍然需要您实现这些方法)我说,咬紧牙关,正确地做,避免“魔术”。对不起,我想我现在完全理解这个问题了。您需要在非泛型调用之间链接泛型信息。是的,尝试创建一个单独的生成器类。可以这样做,但是,有很多噪音。虽然有许多不同的类似Bar()的方法,但大多数情况下用户只调用一个,这将导致.Bar(foo=>foo.Bar())无效:/Through->upvoted,谢谢您的尝试。
void FooBar(this IBar bar, Action<object> action) { /* ... */ }

.
.
.

api.Foo(() => default(long))
   .Bar()
   .FooBar(x => { }); // <-- That will compile, but x is an object
.
.
.FooBar(x => { if (x is MyType) { /* ... */ } });
public interface IFluentApi {}

public interface IFoo<T> {}

public interface IBar<T> {}

public struct Unit {}

public static class Extenders
{
    public static IFoo<Unit> Foo(this IFluentApi api, Action action) {return null;}

    public static IFoo<T> Foo<T>(this IFluentApi api, Func<T> func) {return null;}

    public static IBar<T> Bar<T>(this IFoo<T> foo) {return null;}

    public static void FooBar<T>(this IBar<T> bar, Action action) {}

    public static void FooBar<T>(this IBar<T> bar, Action<T> action) {}

    public static void CheckType<T>(this T value) {}
}

public class Examples
{
    public void Example()
    {

        IFluentApi api = null;

        api.Foo(() => "Value")
           .Bar()
           .FooBar(x => x.CheckType<string>()); // x is a string


        api.Foo(() => {})
           .Bar()
           .FooBar(x => x.CheckType<Unit>() ); // x is a Unit

        // The following (correctly) fails to type check
        Action<string> stringAction = Console.WriteLine;
        api.Foo(() => (long) 7)
           .Bar()
           .FooBar(stringAction); // x should be of type long
    }
}