1

假设我有这段代码

public interface IFoo
{
}

public abstract class FooBase<TModel> : IFoo
{
    public T Create<T>() where T : TModel;
}

public class Foo : FooBase<ModelBase>
{
    public TModel Create<TModel>()
    {
        return Activator.CreateInstance(typeof(TModel));
    }
}

public abstract class ModelBase
{
}

public class ModelFoo : ModelBase
{
}

public class ModelBar : ModelBase
{
}

现在,假设我有这门课

public static FooProvider
{
    public IFoo Get<TModel>()
    {
        var provider = ...; // find which IFoo class has generic TModel
        return provider;
    }
}

我可以设法用

IFoo provider = FooProvider.Get<ModelBar>();  // -> instance of Foo

但我得到一个实例IFoo,因此无权访问这些FooBase方法。是否可以实现IFoo(或中间抽象类)以提供无需(必须)转换返回值即可调用的方法声明provider

理想情况下,我希望能够做到

ModelBar bar = FooProvider.Get<ModelBar>().Create<ModelBar>();

可能吗?

4

2 回答 2

2

随着问题的发展,我发布了第二个答案。这是一个示例类层次结构:

  • 允许对许多模型类型使用相同的提供者
  • 允许以一致的方式构建许多不同的提供者和模型
  • 在编译时检查提供者/模型的兼容性
  • 使用过程中不需要铸造

代码:

public interface IWhatever { }

public class Foo { }
public class Foo2 : Foo, IWhatever { }

public class Bar { }
public class Bar2 : Bar { }
public class Bar3 : Bar, IWhatever { }


public interface IModelProvider<T>
{
    U Create<U>() where U : T;
}

public class FooProvider : IModelProvider<Foo>
{
    public U Create<U>() where U : Foo
    {
        // create a proper "U" - for example Foo or Foo2
        return Activator.CreateInstance<U>(); // simpliest
    }
}

public class BarProvider : IModelProvider<Bar>
{
    public U Create<U>() where U : Bar
    {
        // create a proper "U" - for example Bar, Bar2 or Bar3
        // more verbose
        if (typeof(U) == typeof(Bar)) return (U)new Bar();
        if (typeof(U) == typeof(Bar2)) return (U)(object)new Bar2();
        if (typeof(U) == typeof(Bar3)) return (U)(object)new Bar3();

        throw new Exception();
    }
}

public class WhateverProvider : IModelProvider<IWhatever>
{
    public U Create<U>() where U : IWhatever, new()
    {
        // create a proper "U" - for example Foo2 or Bar3
        return new U(); // really the simpliest
    }
}

    公共类 VeryGenericProvider : IModelProvider { public TModel Create() where TModel : new() { return new TModel(); } }

public class ProviderFactory
{
    public static IModelProvider<T> Get<T>() where T : new()
    {
        // somehow choose a provider for T, dumb implementation just for example purposes
        if (typeof(T) == typeof(Foo)) return (IModelProvider<T>)new FooProvider();
        if (typeof(T) == typeof(Bar)) return (IModelProvider<T>)new BarProvider();
        if (typeof(T) == typeof(IWhatever)) return (IModelProvider<T>)new WhateverProvider();

        return VeryGenericProvider<T>();
    }
}

public static class ProviderTest
{
    public static void test()
    {
        Foo foo = ProviderFactory.Get<Foo>().Create<Foo>();
        Foo2 foo2 = ProviderFactory.Get<Foo>().Create<Foo2>();

        // Bar2 bar2 = ProviderFactory.Get<Foo>().Create<Bar2>(); - compile error
        Bar2 bar2 = ProviderFactory.Get<Bar>().Create<Bar2>(); // - ok!

        Bar3 bar3 = ProviderFactory.Get<IWhatever>().Create<Bar3>();
    }
}

此代码编译,但我还没有运行它。请注意,“测试”中的最终用法确实执行类型检查并且不需要任何强制转换——但提供者的内部实现肯定需要强制转换一些东西——例如,请参阅手动构造对象的 BarProvider。即使“我们知道”确定 U 是 Bar2,也没有办法告诉语言将 U 的约束从 Bar 升级到 Bar2 - 因此需要双重转换。

我的意思是:

return (U)new Bar(); // is ok because U is constrained to 'Bar'

相对

return (U)new Bar2();         // will not compile, U:Bar is not related to Bar2:Bar
return (U)(object)new Bar2(); // is ok: temporary cast to object 'erases' type information
于 2013-05-09T09:26:07.680 回答
1

你的意思

ModelBar bar = FooProvider.Get<ModelBar>().Create<ModelBar>();

更确切地说

ModelBar bar = FooProvider.Get<ModelBar>().Create();

?

我觉得前者有点奇怪,除非你有一些情况,其中第一个 ModelBar 实际上是 ModelBarBase 或 IModelBar 而后者 ModelBar 实际上是最终的可构造类型。

无论如何,我也发现你的类布局有点奇怪,因为在你的代码示例中,Get() 和 Create() 来自同一个类(Foo/FooBase)。我认为你在这里搞砸了 SRP。

我建议:

public class FooProvider
{
    public static Provider<T> Get<T> { return new Provider<T>(); };
}

public class Provider<T>
{
    public T CreateDirect()
    {
        return Activator.Create<T>();
    }

    public TDerived CreateDerived<TDerived>() where TDerived : T
    {
        return Activator.Create<TDerived>();
    }
}

在此设置中,您可以:

class IMyInterf {}
class MyType : IMyInterf {}
class MyChildType : MyType {}

MyType tmp1 = FooProvider.Get<MyType>().CreateDirect();

MyChildType tmp1 = FooProvider.Get<MyType>().CreateDerived<MyChildType>();

MyChildType tmp1 = FooProvider.Get<IMyInterf>().CreateDerived<MyChildType>();

这只是草图,手写没有编译,但它显示了大致的想法。

如果您删除“CreateDirect”并将“CreateDerived”重命名为 Create,您将获得与您的代码类似的内容,但更简单。当然,如果您真的需要,您现在可以将这两个单独的类混合并压缩为一个,但我认为这没有真正意义:)

编辑:

当然,为了更容易构建/找到合适的提供者,Provider<T>您可以引入 aProviderBase<T>和 multiple而不是,但实际仍然必须返回实际的or 。OrangeProvider:ProviderBase<Orange>Get<>ProviderBase<T>IProvider<T>

于 2013-05-09T08:54:58.077 回答