Reputation: 619
public interface IParser<T> where T: new()
{
IList<T> Parse();
}
Above interface is implemented by following abstract class
public abstract class BaseParser<T>: IParser<T> where T : new()
{
protected abstract string Sql { get;}
public List<T> Parse()
{
// do parsing
Console.WriteLine(Sql);
}
}
Following are two concrete implementation of above abstract class
public class EMailParser: BaseParser<Email>
{
protected override string Sql
{
get
{
return @"SELECT * FROM emails";
}
}
}
public class UrlParser : BaseParser<Url>
{
protected override string Sql
{
get
{
return @"SELECT * From Url";
}
}
}
Usage:
class Program
{
static void Main(string[] args)
{
if(args[1] == "url")
Parser<Url>();
else
Parser<Email>();
}
static void Parse<T>()
{
// Create instance based on typof T and then assign to implementaion
IParser<T> parser = typeof(T) == typeof(Url) ? new UrlParser(): new EmailParser();
parser.Parse();
}
}
I want to create instance of EmailParser
or UrlParser
base on generic type provided in Program.Main
method and assign it to interface implemented by BaseParser
(abstract class). How can I do this? I know i can solve this problem by modifying Program.Parse<T>
as following
static void Parse<T>() where T: new()
{
IParser<T> parser = typeof(T) == typeof(Url) ? new UrlParser() as BaseParser<T> : new EmailParser() as BaseParser<T>;
parser.Parse();
}
However I want to know why I can't assign child class instance to interface implemented by abstract class??
I can't understand why following line does not work
IParser<T> parser = typeof(T) == typeof(Url) ? new UrlParser(): new EmailParser();
and why this line work
IParser<T> parser = typeof(T) == typeof(Url) ? new UrlParser() as BaseParser<T> : new EmailParser() as BaseParser<T>;
As per @nawfal answer this line also should not work because BaseParser and BaseParser are different types. Does there exists implicit case for IParser from BaseParser?
Upvotes: 4
Views: 306
Reputation: 73283
Because there is no implicit conversion between UrlParser
and EmailParser
. They both go back to BaseParser<Url>
and BaseParser<Email>
(or IParser<Url>
and IParser<Email>
) respectively, both of which are different types as far as compiler is concerned.
I would keep a dictionary to hold a map of type information and then use reflection. Something like (not tested):
static Dictionary<string, Type> typeInfos = new Dictionary<string, Type>
{
{ "url", typeof(Url) },
{ "email", typeof(Email) },
// and so on
};
And you do,
class Program
{
static void Main(string[] args)
{
Parse(args[1]);
}
static void Parse(string type)
{
var parserType = typeof(BaseParser<>)
.Assembly // or whatever the assembly is
.GetTypes()
.First(t => t.BaseType?.GetGenericArguments().FirstOrDefault() == typeInfos[type]);
dynamic parser = Activator.CreateInstance(parserType);
parser.Parse();
}
}
Use expression trees and/or cache things to make things faster.
Update: No, BaseParser<T>
and BaseParser<T>
are the exact same thing. T
can only have one value at a time in your generic method. The real question is how can you cast new UrlParser() as BaseParser<T>
. In a generic context it is possible to cast anything to anything using as
C# rules are bit liberal there (I dont know the which part exactly in spec).
Upvotes: 2
Reputation: 1743
I believe the issue is that the compiler doesn't consider the type to which you are assigning the result of the ?: conditional when parsing the conditional. Rather, the ?: is parsed in isolation, so the compiler can't figure out which type to use.
Edit: From section 7.14 of the C# 5.0 specification:
The second and third operands, x and y, of the ?: operator control the type of the conditional expression. If x has type X and y has type Y then:
If an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
If an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
Otherwise, no expression type can be determined, and a compile-time error occurs.
Upvotes: 4
Reputation: 4408
You can use a dynamic dispatch:
static IParser<Url> CreateParser(Url uri)
{
return new UrlPaqrser<Url>();
}
static IParser<Email> CreateParser(Email mail)
{
return new EmailPaqrser<Email>();
}
static void Main(string[] args)
{
dynamic t = new Url();
var parser = CreateParser(t); // invokes CreateParser(Url uri)
t = new Email();
parser = CreateParser(t); // invokes CreateParser(Email email)
}
Upvotes: 1