简体   繁体   English

C#使用泛型方法为泛型类创建接口

[英]C# create an interface for a generic class with generic methods

Let say I have this piece of code 假设我有这段代码

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

Now, let say I have this class 现在,让我说我有这门课

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

And I can manage to call this with 我可以设法用这个来打电话

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

But I get an instance of IFoo , therefore don't have access to the FooBase methods. 但是我得到了一个IFoo实例,因此无法访问FooBase方法。 Is it possible to implement IFoo (or an intermediary abstract class) to provide a method declaration that can be called without (necessarily) casting the return value provider ? 是否可以实现IFoo (或中间抽象类)来提供可以在没有(必然)转换返回值provider情况下调用的方法声明?

Ideally, I'd like to be able to do 理想情况下,我希望能够做到

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

Is it possible? 可能吗?

As the question evolved, I'm posting second answer. 随着问题的演变,我发布了第二个答案。 Here's a sample class hierarchy which: 这是一个示例类层次结构:

  • allows to use same provider for many model types 允许对许多模型类型使用相同的提供程序
  • allows to construct many different providers and models in a consistent way 允许以一致的方式构建许多不同的提供者和模型
  • checks provider/model compatibility at compile time 编译时检查提供程序/模型兼容性
  • does not need casting during usage 在使用过程中不需要铸造

Code: 码:

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

public class VeryGenericProvider : IModelProvider { public TModel Create() where TModel : new() { return new TModel(); public class VeryGenericProvider:IModelProvider {public TModel Create()其中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>();
    }
}

This code compiles, but I have not run it. 这段代码编译,但我还没有运行它。 Note that the final usage in the "test" does perform type checks and does not need any casts -- but the provider's internal implementation surely will need to cast a few things - for example please see the BarProvider which manually constructs the objects. 请注意,“test”中的最终用法确实执行了类型检查,并且不需要任何强制转换 - 但是提供者的内部实现肯定需要抛出一些东西 - 例如,请参阅手动构造对象的BarProvider。 Even if "we know" for sure that U is Bar2, there is no way to tell the language to upgrade the constraint over U from Bar to Bar2 - hence a double cast is needed. 即使“我们知道”肯定U是Bar2,也没有办法告诉语言将U上的约束从Bar升级到Bar2 - 因此需要双重演员。

I mean this: 我是说这个:

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

versus

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

Did you mean 你的意思是

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

or rather 更确切地说

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

?

I find the former a bit strange unless you have some cases where first ModelBar is actually ModelBarBase or IModelBar and the latter ModelBar is in fact the final constructable type. 我发现前者有点奇怪,除非你有一些情况,其中第一个ModelBar实际上是ModelBarBase或IModelBar,而后一个ModelBar实际上是最终的可构造类型。

Anyways, I also find your class layout a little strange, as in your code example the Get() and Create() come from the same class (Foo/FooBase). 无论如何,我还发现你的类布局有点奇怪,因为在你的代码示例中,Get()和Create()来自同一个类(Foo / FooBase)。 I think you messed up with SRP here. 我觉得你在这里搞砸了SRP。

I'd recommend: 我建议:

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>();
    }
}

and in this setup you can: 在此设置中,您可以:

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>();

This is only sketch, written by hand without compiling, but it shows the general idea. 这只是草图,手工编写而不编译,但它显示了一般的想法。

If you remove "CreateDirect" and rename "CreateDerived" to just Create, you'd get something similar to your code, but simplier. 如果您删除“CreateDirect”并将“CreateDerived”重命名为Create,您将得到与您的代码类似的内容,但更简单。 And of course, you may now mix and squash those two separate classes into one if you really need, but I see no real point in it :) 当然,如果你真的需要的话,你现在可以将这两个单独的类混合并压缩成一个,但是我认为没有真正的意义:)

EDIT: 编辑:

And of course instead of Provider<T> you can introduce a ProviderBase<T> and multiple OrangeProvider:ProviderBase<Orange> just for the sake of easier building/finding the proper provider, but still the actual Get<> will have to return the actual ProviderBase<T> or IProvider<T> . 当然,代替Provider<T>您可以引入ProviderBase<T>和多个OrangeProvider:ProviderBase<Orange>只是为了更容易构建/找到合适的提供程序,但实际的Get<>仍然必须返回实际的ProviderBase<T>IProvider<T>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM