I am using a factory similar to this:
interface I<T>
{
void Print();
}
class A : I<A>
{
public string Id { get; }
public A(string id) { Id = id; }
public void Print() { Console.WriteLine(Id); }
}
class B : I<B>
{
public int Id { get; }
public B(int id) { Id = id; }
public void Print() { Console.WriteLine(Id); }
}
class Factory
{
public T Create<T>()
where T : I<T>
{
if (typeof(T) == typeof(A))
return (T)(I<T>)new A("A");
else if (typeof(T) == typeof(B))
return (T)(I<T>)new B(2);
else
throw new Exception("Unknown className");
}
}
var sut = new Factory();
sut.Create<A>().Print();
sut.Create<B>().Print();
What I do not fully understand is: why is this double cast
(T)(I<T>)new A()
necessary? The compiler knows that
new A("A") is I<A>
and actually knows that
new A("A") is T
BTW: I am using the generic interface, since otherwise
Create<I>()
would compile fine but is not desired here.
felix-b :
Your code reasons that when typeof(T) == typeof(A) is true, it guarantees that the type of expression new A(...) is compatible with T.\n\nThough this reasoning is correct at runtime, C# compiler doesn't treat typeof(T) == typeof(A) as any kind of \"type guard\".\n\n\n The compiler knows that new A(\"A\") isI<A>\n\n\nAgree\n\n\n and actually knows that new A(\"A\") is T\n\n\nNope, it cannot. In template-based approach (e.g. in C++) the \"generic\" function is compiled per every T encountered in source code, and then the compiler knows. However in .NET the generics is runtime-based, so that C# compiler must compile a code that would work for any T complying with the constraints, but besides that the concrete T is not known at compile time.\n\nIf you want to employ compiler \"type guards\" and avoid explicit casts, you can rewrite your code using C# pattern matching:\n\npublic T Create<T>()\n where T : I<T>\n{\n if (typeof(T) == typeof(A) && new A(\"A\") is T retA)\n return retA;\n else if (typeof(T) == typeof(B) && new B(2) is T retB)\n return retB;\n else\n throw new Exception(\"Unknown className\");\n} \n",
2019-07-09T06:26:08
trollingchar :
\n and actually knows that\n \n new A(\"A\") is T\n\n\nNo. If it knows that A is I<A>, does not mean that A and T is the same. T may be B and your cast will fail. Look at this code:\n\npublic T Create<T>()\n where T : I<T>\n{\n if (typeof(T) == typeof(A))\n return (T)(I<T>)new B(2);\n else if (typeof(T) == typeof(B))\n return (T)(I<T>)new A(\"A\");\n else\n throw new Exception(\"Unknown className\");\n}\n\n\nI swapped A and B and you cast B to A. The cast is invalid.",
2019-07-09T06:33:41
DotNet Developer :
\n What I do not fully understand is: why is this double cast\n necessary? The compiler knows that\n\n\nWhat compiler knows is following\n\nA is I<A>\nB is I<B>\nT is I<T>\nT is not A\nT is not I<A>\nT is not B\nT is not I<B>\n\n\nThat's why an instance of T must be cast to parent interface first then down to specific type.",
2019-07-09T06:55:07