views:

111

answers:

4

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();
            }
        }
    }
}
+2  A: 

You cannot get this work as you intended. Type constraints cannot be used to decide between your two methods.

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

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

Assume

public class SomeEntity { }

public class SomeOtherEntity : SomeEntity { }

and SomeOtherEntity is a valid type argument for both methods yielding two methods with identical signature.

The way to go is probably a single generic method that uses the supplied type argument to dispatch the call to the desired implementation. This is in turn probably solved most easily by implementing an interface on all concrete types.

Daniel Brückner
+1 even though it's not the answer I wanted to hear :) Is there any way I can get the signatures to differ without needing to instantiate an object? (I can't instantiate anything because the library class only knows the interface, not the implementation.)
DanM
A: 

The only solution I can think of is to define an IRepositorySource<T> interface that each RepositorySource class can implement explicitly:

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

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

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

To access these methods you'll need to cast a RepositorySource instance into the required interface type e.g.

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

public class RepositorySource {

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

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

}

Vlad
+1  A: 

Constraints are not part of the signature. This fact has numerous ramifications, many of which apparently irk people to no end. For some of those ramifications, and about a million comments telling me that I am WRONG WRONG WRONG, see this article and its accompanying comments.

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

I would solve your problem by having two methods with two different names.

Eric Lippert
+1. Thanks for your answer and for the link, Eric. I managed to achieve the result I was looking for using specifically named methods (as you suggest) and a `Func` parameter that allows me to use lambda expressions to obtain the repository from the source. Not quite as pretty as just being able to specify the type I want, but it basically accomplishes the degree of "generic-ness" I was looking for :)
DanM