Let's say I have a class library that defines a couple entity interfaces:
public interface ISomeEntity { /* ... */ }
public interface ISomeOtherEntity { /* ... */ }
This library also defines an IRepository
interface:
public interface IRepository<TEntity> { /* ... */ }
And finally, the library has an abstract class called RepositorySourceBase
(see below), which the main project needs to implement. The goal of this class is to allow the base class to grab new Repository
objects at runtime. Because certain repositories are needed (in this example a repository for ISomeEntity
and ISomeOtherEntity
), I'm trying to write generic overloads of the GetNew<TEntity>()
method.
The following implementation doesn't compile (the second GetNew()
method gets flagged as "already defined" even though the where clause is different), but it gets at what I'm trying to accomplish:
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;
}
The intended usage of this class would be something like this:
public class RepositorySourceTester
{
public RepositorySourceTester(RepositorySourceBase repositorySource)
{
var someRepository = repositorySource.GetNew<ISomeEntity>();
var someOtherRepository = repositorySource.GetNew<ISomeOtherEntity>();
}
}
Meanwhile, over in my main project (which references the library project), I have implementations of ISomeEntity
and ISomeOtherEntity
:
public class SomeEntity : ISomeEntity { /* ... */ }
public class SomeOtherEntity : ISomeOtherEntity { /* ... */ }
The main project also has an implementation for IRepository<TEntity>
:
public class Repository<TEntity> : IRepository<TEntity>
{
public Repository(string message) { }
}
And most importantly, it has an implementation of the abstract 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");
}
}
Just as with RepositorySourceBase
, the second GetNew()
method gets flagged as "already defined".
So, C# basically thinks I'm repeating the same method because there's no way to distinguish the methods from their parameters alone, but if you look at my usage example, it seems like I should be able to distinguish which GetNew()
I want from the generic type parameter, e.g, <ISomeEntity>
or <ISomeOtherEntity>
).
What do I need to do to get this to work?
Update
I ended up solving this using specifically-named methods and a Func<T, TResult>
parameter.
So, RepositorySourceBase
now looks like this:
public abstract class RepositorySourceBase
{
public abstract Repository<ISomeEntity> GetNewSomeEntity();
public abstract Repository<ISomeOtherEntity> GetNewSomeOtherEntity();
}
And RepositorySource
looks like this:
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");
}
}
Now, what started this whole thing off was that I needed a generic RepositoryUtilizer
class that could grab a repository from a source simply by knowing the type of repository (which could be specified as a generic type parameter). Turns out, that wasn't possible (or at least not easily possible). However, what is possible is to use the Func<T, TResult>
delegate as a parameter to allow the RepositoryUtilizer
class to obtain the repository without needing to "know" the method name.
Here's an example:
public class RepositoryUtilizer
{
public DoSomethingWithRepository<TEntity>(
Func<TRepositorySource, IRepository<TEntity>> repositoryGetter)
{
using (var repository = repositoryGetter(RepositorySource))
{
return repository.DoSomething();
}
}
}
}