Search code examples
c#genericsmethodsinterfacegeneric-method

I am getting weird errors while implementing a generic method of an interface in C#. What exactly is going wrong here?


public interface IGet<T>
{
    T Get<K>(K id);
}

public interface IDemoRepository : IGet<Dto.Message>
{

}

public class DemoRepository : IDemoRepository
{
    public Dto.Message Get<string>(string messageId)
    {
        using (var db = new AppDbContext())
        {
            return db.Messages
                .FirstOrDefault(f => f.MessageId.ToLower() == messageId.ToLower());
        }
    }
}

Below are the errors in the Error List.

Severity    Code    Description
Error       CS1001  Identifier expected
Error       CS1003  Syntax error, '>' expected  
Error       CS1003  Syntax error, '(' expected  
Error       CS1001  Identifier expected 
Error       CS1003  Syntax error, ',' expected  
Error       CS1003  Syntax error, ',' expected  
Error       CS8124  Tuple must contain at least two elements.   
Error       CS1001  Identifier expected 
Error       CS1026  ) expected  
Error       CS0535  'DemoRepository' does not implement interface member 'IGet<Dto.Message>.Get<K>(K)'  
Error       CS0103  The name 'messageId' does not exist in the current context  

A .NET fiddle with success and fail scenarios here


Solution

  • The reason why the compiler gave out so many errors is because it expected an identifier in the <>, but you put a keyword (string) there. The compiler got confused as a result, and the parsing got very wrong. This is also why there are way fewer errors if you use Guid, because Guid is an identifier. But replacing string with Guid doesn't do what you expect either.

    You seem to want DemoRepository to only implement IDemoRepository in a very specific case, i.e. when K is string.

    Note that the requirement for implementing IDemoRepository is that it should have a generic Get method, being able to take any type K, and returns a Dto.Message. That is what's meant by:

    public interface IGet<T>
    {
        T Get<K>(K id);
    }
    
    public interface IDemoRepository : IGet<Dto.Message>
    {
    
    }
    

    I suppose that's not what you actually want IDemoRepository to require? If you only want it to require a non generic Get method. that takes a specific type K, which can be chosen by the implementer, then you can do something like this:

    // I moved the generic parameter K from the method to the interface
    public interface IGet<T, K>
    {
        T Get(K id);
    }
    
    public interface IDemoRepository<K> : IGet<Dto.Message, K>
    {
    
    }
    
    // here the implementer chose string as K
    public class DemoRepository : IDemoRepository<string>
    {
        public Dto.Message Get(string messageId)
        {
            ...
        }
    }