1

I trying to use factory pattern to get instance of a class using generics. Not sure what I'm missing the code below.

namespace ConsoleApplication1
{
    public interface IResult
    {
    }

    public class ResultA : IResult
    {
        public string P1 { get; set; }
    }

    public interface IService<T> where T : IResult
    {
        T DoWork();
    }

    public class ServiceA<T> : IService<T> where T : ResultA, IResult, new()
    {
        public T DoWork()
        {
            var t = new T();
            t.P1 = "value1";
            return t;
        }
    }

    public interface IFactory
    {
        T GetService<T>(string documentType) where T : IService<IResult>;
    }

    public class MyFactory : IFactory
    {
        public T GetService<T>(string documentType) where T : IService<IResult>
        {
            // based on documenet type I will be returning the instances of service here
            // im getting error at this line
            return new ServiceA<ResultA>();                }
    }

}

Error 1 Cannot implicitly convert type 'ConsoleApplication1.ServiceA' to 'T'

Update1
this didn't work either

public interface IFactory
{
    IService<IResult> GetService(string documentType);
}

public class MyFactory : IFactory
{
    public IService<IResult> GetService(string documentType)
    {
        return new ServiceA<ResultA>();
    }
}
LP13
  • 30,567
  • 53
  • 217
  • 400

3 Answers3

3

Given that you know that in every IServiceA, T implements IResult, you don't need to make your Factory Generic, but just bound it to return IService (as indicated by the prev. answer). But, in order to make the code compile, you'll need to cast the return in MyFactory.GetService, as such:

    ...
    public interface IFactory
    {
        IService<IResult> GetService(string documentType);
    }

    public class MyFactory : IFactory
    {
        public IService<IResult> GetService(string documentType)
        {
            //Cast needed to address the error...
            return (IService<IResult>) new ServiceA<ResultA>();
        }
    }

This code should be equivalent to what you are trying to do, and should compile correctly.

  • not sure why do I need to cast it when ServiceA is derived from IService – LP13 Aug 15 '16 at 21:53
  • Probably because the compiler has not yet compiled your "where". – DaniDev Aug 15 '16 at 22:07
  • Well, in a way, it is a limitation of what's possible for the compiler. It has to do with combined generic variant types (see here: https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx). The problem is that the compiler can know that IService can be cast from ServiceA, but when you also use an interface as parameter as in IService, it can't guarantee that it would be type safe, and thus it requires that you cast it explicitly to make sure that you know that the class is safe to cast. – Milton Hernandez Aug 15 '16 at 22:07
  • There is more info about this here: https://blogs.msdn.microsoft.com/csharpfaq/2010/02/16/covariance-and-contravariance-faq/. Specifically: "If you implement a variant generic interface, the implementing class is still invariant. Classes and structs do not support variance in C# 4.0. " So the following doesn’t compile: // List implements the covariant interface // IEnumerable. But classes are invariant. List list = new List(); // Compiler error here. – Milton Hernandez Aug 15 '16 at 22:21
0

Every where you have T , replace it with IResult

public interface IService<IResult>
{
    IResult DoWork();
}

public class ServiceA<IResult> : IService<IResult> {}



public interface IFactory
{
    IResult GetService(string documentType);
}
Shachaf.Gortler
  • 5,655
  • 14
  • 43
  • 71
0

You need to to cast your result. I also think the compiler need to compile your "where" clause, which seems an IEnumerable(from my experience).

Dexter
  • 69
  • 1
  • 10