public abstract class ViewBase { }
public abstract class ViewBase<T0, T1> : ViewBase
{
public abstract void Initialize(T0 arg0, T1 arg1);
}
public static class Navigator
{
public static void Navigate<TView, T0, T1>(T0 arg0, T1 arg1) where TView : ViewBase<T0, T1>
{
var view = (ViewBase<T0, T1>)Activator.CreateInstance<TView>();
view.Initialize(arg0, arg1);
/* ... */
}
}
上面的代码应该可行,但每次调用Navigate时我都不喜欢通用类型的长列表:
Navigate<DerivedView, string, int>("Joe", 42);
假设派生类:
public class DerivedView : ViewBase<string, int>
{
/* ... */
}
在genereal中,有可能减少到:
Navigate<DerivedView>("Joe", 42);
因为类型信息是多余的。
你有什么建议吗? 我缺少可变参数模板;)
由于
答案 0 :(得分:1)
类型推断是全有或全无的。如果指定了任何类型参数,则必须全部指定它们。因此,您需要一种方法:
TView
TView
作为类型参数T0
和T1
作为类型参数您可以通过传入工厂函数来指定TView
的类型来推断TView
:
public static void Navigate<TView, T0, T1>(Func<TView> factory, T0 arg0, T1 arg1)
where TView : ViewBase<T0, T1> {
var view = factory();
view.Initialize(arg0, arg1);
// ...
}
现在,请拨打Navigate<DerivedView>("Joe", 42)
。
Navigate(() => new DerivedView(), "Joe", 42)
注意:这要求arg0
和arg1
的类型与TView
指定的内容完全匹配。隐式转换不起作用。如果视图派生自ViewBase<int, object>
,arg1
为"Hello"
,那么T1
将被推断为类型string
,从而导致编译器错误消息。
由于除了构造实例之外,您避免使用TView
的类型,因此您也可以通过使用工厂函数避免将TView
作为类型参数:
public static void Navigate<T0, T1>(Func<ViewBase<T0, T1>> factory, T0 arg0, T1 arg1) {
var view = factory();
view.Initialize(arg0, arg1);
// ...
}
这也避免了隐式转化的问题,因为T0
和T1
都可以从factory
中推断出来。
至于避免T0
和T1
作为类型参数:名称Initialize
稍微暗示其功能可能属于构造函数。如果是这种情况,您可以通过将构造留给调用者来简单地将它们作为类型参数来避免:工厂函数可以是() => new DerivedView("Joe", 42)
。但是,如果您不是这种情况,如果您真的需要一个由Initialize
调用的单独Navigate
方法,那么我没有看到任何避免T0
和{{{{}}}的选项1}}作为类型参数。
最后,拆分类型参数:
T1
这允许您致电public static class Navigator {
public static WithViewHelper<TView> WithView<TView>()
where TView : new() => new WithViewHelper<TView>();
public struct WithViewHelper<TView> where TView : new() { }
}
public static class NavigatorExtensions {
public static void Navigate<TView, T0, T1>(this Navigator.WithViewHelper<TView> withViewHelper, T0 arg0, T1 arg1)
where TView : ViewBase<T0, T1>, new() {
var view = new TView();
view.Initialize(arg0, arg1);
}
}
。它需要Navigator.WithView<DerivedView>().Navigate("Joe", 42)
作为扩展方法,否则无法表达泛型类型约束。隐式转换再次出现问题:Navigate
将无法编译:即使Navigator.WithView<DerivedView>().Navigate(null, 42)
可转换为null
,编译器也不会发现string
应该是{{} 1}}。
答案 1 :(得分:0)
我想你想要的是一个归结为:
的约束 class Generic<T> where T : Generic2<T1, T2>
并且可以访问您的通用成员签名中的T1,T2。
不幸的是,C#目前不支持。
答案 2 :(得分:0)
除了提出的方法,我想出了从ViewBase类创建对象:
public abstract class ViewBase<T0, T1>
{
public abstract void Initialize(T0 arg0, T1 arg1);
public static T Navigate<T>(T0 arg0, T1 arg1) where T : ViewBase<T0, T1>
{
var view = Activator.CreateInstance<T>();
view.Initialize(arg0, arg1);
return view;
}
}
这允许以下呼叫:
DerivedView.Navigate<DerivedView>("Joe", 42);
但这也带有冗余代码..或:
ViewBase.Navigate<DerivedView>("Joe", 42);
答案 3 :(得分:0)
还考虑使用通用工厂。我缓存构造函数调用签名,以便第二次更快地检索。
public static class Factory
{
//store constructors for type T
static Dictionary<string, ConstructorInfo> ctors=new Dictionary<string, ConstructorInfo>();
public static T New<T>(params object[] args)
{
var arg_types=args.Select((obj) => obj.GetType());
var arg_types_names=args.Select((obj) => obj.GetType().Name);
string key=string.Format("{0}({1})", typeof(T).Name, string.Join(",", arg_types_names);
if(!ctors.ContainsKey(key))
{
// if constructor not found yet, assign it
var ctor=typeof(T).GetConstructor(arg_types.ToArray());
if(ctor==null)
{
throw new MissingMethodException("Could not find appropriate constructor");
}
ctors[key]=ctor;
}
// invoke constructor to create a new T
return (T)ctors[key].Invoke(args);
}
}