麻烦抽象泛型方法

时间:2010-05-14 23:18:56

标签: c# generics fluent

假设我有一个定义了几个实体接口的类库:

public interface ISomeEntity { /* ... */ }
public interface ISomeOtherEntity { /* ... */ }

该库还定义了IRepository接口:

public interface IRepository<TEntity> { /* ... */ }

最后,该库有一个名为RepositorySourceBase的抽象类(见下文),主要项目需要实现。此类的目标是允许基类在运行时获取新的Repository对象。因为需要某些存储库(在此示例中是ISomeEntityISomeOtherEntity的存储库),我正在尝试编写GetNew<TEntity>()方法的泛型重载。

以下实现不编译(第二个GetNew()方法被标记为“已定义”,即使where子句不同),但它得到了我正在努力实现的目标:

public abstract class RepositorySourceBase // This doesn't work!
{
    public abstract Repository<TEntity> GetNew<TEntity>()
        where TEntity : SomeEntity;
    public abstract Repository<TEntity> GetNew<TEntity>()
        where TEntity : SomeOtherEntity;
}

这个类的预期用法是这样的:

public class RepositorySourceTester
{
    public RepositorySourceTester(RepositorySourceBase repositorySource)
    {
        var someRepository = repositorySource.GetNew<ISomeEntity>();
        var someOtherRepository = repositorySource.GetNew<ISomeOtherEntity>();
    }
}

同时,在我的主项目(引用库项目)中,我实现了ISomeEntityISomeOtherEntity

public class SomeEntity : ISomeEntity { /* ... */ }
public class SomeOtherEntity : ISomeOtherEntity { /* ... */ }

主项目还有IRepository<TEntity>的实施:

public class Repository<TEntity> : IRepository<TEntity>
{
    public Repository(string message) { }
}

最重要的是,它具有抽象RepositorySourceBase

的实现
public class RepositorySource : RepositorySourceBase
{
    public override IRepository<ISomeEntity> GetNew()
    {
        return new (IRepository<ISomeEntity>)Repository<SomeEntity>(
            "stuff only I know");
    }

    public override IRepository<ISomeOtherEntity> GetNew()
    {
        return new (IRepository<ISomeEntity>)Repository<SomeOtherEntity>(
            "other stuff only I know");
    }
}

RepositorySourceBase一样,第二个GetNew()方法被标记为“已定义”。


所以,C#基本上认为我正在重复相同的方法,因为没有办法单独区分方法和参数,但是如果你看一下我的用法示例,看起来我应该是能够区分我想要的GetNew()泛型类型参数,例如<ISomeEntity><ISomeOtherEntity>)。

我需要做些什么才能让它发挥作用?


更新

我最终使用特定命名的方法和Func<T, TResult>参数解决了这个问题。

所以,RepositorySourceBase现在看起来像这样:

public abstract class RepositorySourceBase
{
    public abstract Repository<ISomeEntity> GetNewSomeEntity();
    public abstract Repository<ISomeOtherEntity> GetNewSomeOtherEntity();
}

RepositorySource看起来像这样:

public class RepositorySource : RepositorySourceBase
{
    public override IRepository<ISomeEntity> GetNewSomeEntity()
    {
        return new (IRepository<ISomeEntity>)Repository<SomeEntity>(
            "stuff only I know");
    }

    public override IRepository<ISomeOtherEntity> GetNewSomeOtherEntity()
    {
        return new (IRepository<ISomeEntity>)Repository<SomeOtherEntity>(
            "other stuff only I know");
    }
}

现在,开始这一切的是我需要一个通用RepositoryUtilizer类,只需知道存储库的类型(可以指定为泛型类型参数)就可以从源中获取存储库。事实证明,这是不可能的(或者至少不可能)。但是, 可能是使用Func<T, TResult>委托作为参数,以允许RepositoryUtilizer类获取存储库而无需“知道”方法名称。

以下是一个例子:

public class RepositoryUtilizer
{
    public DoSomethingWithRepository<TEntity>(
        Func<TRepositorySource, IRepository<TEntity>> repositoryGetter)
        {
            using (var repository = repositoryGetter(RepositorySource))
            {
                return repository.DoSomething();
            }
        }
    }
}

4 个答案:

答案 0 :(得分:2)

你不能像预期的那样得到这项工作。类型约束不能用于决定两种方法之间的关系。

public abstract Repository<TEntity> GetNew<TEntity>()
    where TEntity : SomeEntity;

public abstract Repository<TEntity> GetNew<TEntity>()
    where TEntity : SomeOtherEntity;

假设

public class SomeEntity { }

public class SomeOtherEntity : SomeEntity { }

SomeOtherEntity是两个方法的有效类型参数,产生两个具有相同签名的方法。

可行的方法可能是使用提供的类型参数将调用分派到所需实现的单个泛型方法。反过来,这可能最容易通过在所有具体类型上实现接口来解决。

答案 1 :(得分:1)

约束不是签名的一部分。这个事实有很多分歧,其中许多显然让人无法忍受。对于其中一些分支,以及大约一百万条评论告诉我错误的错误,请参阅本文及其附带的评论。

http://blogs.msdn.com/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

我会通过两种不同名称的方法解决您的问题。

答案 2 :(得分:0)

我能想到的唯一解决方案是定义每个RepositorySource类可以显式实现的IRepositorySource<T>接口:

public interface IRepositorySource<T>
{
    IRepository<T> GetNew();
}

public class RepositorySource : IRepositorySource<ISomeEntity>, IRepositorySource<ISomeOtherEntity>
{
    IRepository<ISomeEntity> IRepositorySource<ISomeEntity>.GetNew()
    {
        ...
    }

    IRepository<ISomeOtherEntity> IRepositorySource<ISomeOtherEntity>.GetNew()
    {
        ...
    }
}

要访问这些方法,您需要将RepositorySource实例强制转换为所需的接口类型,例如

IRepository<IEntity> r = ((IRepositorySource<IEntity>)repositorySource).GetNew();

答案 3 :(得分:0)

公共类RepositorySource {

static IRepository<T> IRepositorySource.GetNew<T>()

{
    if (typeof(T) == typeof(ISomeEntity))
       return (IRepository<T>)new SomeEntityRepository();
    ...
}

}