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
}
}