C# generic interface type inference question - c#

I'm not sure how to phrase this question concisely without just giving the example so here goes:
public interface IThing<T>
{
void Do(T obj);
}
public class ThingOne : IThing<int>
{
public void Do(int obj)
{
}
}
public class ThingTwo : IThing<string>
{
public void Do(string obj)
{
}
}
public class ThingFactory
{
public IThing<T> Create<T>(string param)
{
if (param.Equals("one"))
return (IThing<T>)new ThingOne();
if (param.Equals("two"))
return (IThing<T>)new ThingTwo();
}
}
class Program
{
static void Main(string[] args)
{
var f = new ThingFactory();
// any way we can get the compiler to infer IThing<int> ?
var thing = f.Create("one");
}
}

The question appears to be here:
// any way we can get the compiler to infer IThing<int> ?
var thing = f.Create("one");
No. You would need to explicitly specify the type:
var thing = f.Create<int>("one");
You can't infer the return type without having a parameter used specifically in the method. The compiler uses the parameters passed to the method to infer the type T, and in this case, it's a single string parameter, with no parameters of type T. As such, there's no way to have this inferred for you.

No, you can't do this because the result of your Create factory method will be evaluated at runtime based on the value of the parameter. Generics are for compile-time safety and in your case you cannot have such safety because the parameter value will be known only at runtime.

Related

Can we use a null instance of a class with a generic-typed property without declaring the Type of that property?

If a class has a property that is of a generic type (Like Thingus<T> below), is it possible to use a null instance of that class without specifying the type of the generically-typed property for that object?
using System;
public class Program
{
public static void Main()
{
DoThing(new Thingus<int>
{
Name = "Integer thingus",
Something = 42
});
//Is there a way to make this function work without the function on line 19?
//If thingus is null then I don't care what the Type is for Something.
//If not, does the type used in the function on line 19 matter for performance at all?
DoThing();
}
public static void DoThing(Thingus<string> thingus = null) => DoThing<string>(thingus);
public static void DoThing<T>(Thingus<T> thingus)
{
if(thingus is null)
{
Console.WriteLine("Oh look! The thingus is null!");
}
else
{
Console.WriteLine($"Thingus's Something is {thingus.Something}");
}
}
}
public class Thingus<T>
{
public string Name { get; set; }
public T Something { get; set; }
}
To see why it is probably impossible, consider this code snippet
public static void DoThing<T>(Thingus<T> thingus)
{
Console.WriteLine(typeof(T));
}
If you write DoThing(null), there is no hints that which type T you want to use, so it is impossible for the compiler to make it works because with just DoThing(null) you are providing not enough information for the compiler.
You don't need to specify the type parameter on your first call, because the compiler can infer it from the int on the type parameter in the argument. In your second method, it can't infer that, because you want to send null.
You can drop the method you want to drop, and call your generic method with some irrelevant type parameter to satisfy the compiler:
DoThing<Thingus<int>>(null);

Converting generic type

I have two generic classes implementing one interface.
public interface Interface1
{
//implementation
}
public interface Interface2<T>
{
//implementation
}
class Class1<T>: Interface2<T> where T : Interface1
{
//implementation
}
class Class2<T>: Interface2<T>
{
//implementation
}
I would like to write a method that returns object of one of these classes, depending on the type T.
Interface2<T> GetObject<T>()
{
if (typeof(Interface1).IsAssignableFrom(typeof(T)))
{
//error
return new Class1<T>();
}
return new Class2<T>();
}
Implementation of Class1 must be limited to types implementing interface. Is there a way to convert type T to Interface1? Now I am obtaining error: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Class1'. There is no boxing conversion or type parameter conversion from 'T' to 'Test.Interface1'.
Full reflection would be:
return (Interface2<T>)Activator.CreateInstance(typeof(Class1<>).MakeGenericType(typeof(T)));
but it is slow (slow compared to doing a new Foo())... I don't find any other way. Note that you are already going partially in the reflection direction (the IsAssignableFrom)
Mmmh using the "caching" of static classes, we can cheat a little... We can produce at runtime the exact code needed for creating a new Class1<T> and cache it.
First version
static class Maker<T>
{
public static Func<Interface2<T>> Func { get; private set; }
public static Interface2<T> New()
{
if (Func == null)
{
Func = Expression.Lambda<Func<Interface2<T>>>(Expression.New(typeof(Class1<>).MakeGenericType(typeof(T)))).Compile();
}
return Func();
}
}
I use an expression tree that does the new Class1<T>. Then:
static Interface2<T> GetObject<T>()
{
if (typeof(Interface1).IsAssignableFrom(typeof(T)))
{
return Maker<T>.New();
}
return new Class2<T>();
}
But still we can do something more. Given a type T, the result of the if in GetObject() can be precalculated and cached. We move the whole GetObject() inside the expression tree.
static class Maker2<T>
{
public static Func<Interface2<T>> Func { get; private set; }
public static Interface2<T> New()
{
if (Func == null)
{
if (typeof(Interface1).IsAssignableFrom(typeof(T)))
{
Func = Expression.Lambda<Func<Interface2<T>>>(Expression.New(typeof(Class1<>).MakeGenericType(typeof(T)))).Compile();
}
else
{
Func = Expression.Lambda<Func<Interface2<T>>>(Expression.New(typeof(Class2<>).MakeGenericType(typeof(T)))).Compile();
}
}
return Func();
}
}
and then
static Interface2<T> GetObject2<T>()
{
return Maker2<T>.New();
}
The solution that uses an expression tree is very slow the first time it is used for each type T, because it has to produce the expression tree and compile it, but then it becomes very fast. This compared to the version that uses the Activator.CreateInstance that is slow every time :-)

Implicit conversions not considered in overload resolution?

I'm trying to wrap a type (outside of my control) so that it would seamlessly appear to implement an interface (also outside of my control).
Given these defintions
// External types. Not changable.
class Foo {
public int I { get; set; }
public int J { get; set; }
}
interface IGenerateSignature {
string Generate();
}
I would like to use a Foo instance to call a method with an IGenerateSignature parameter:
void Test() {
var foo = new Foo { I = 1, J = 2 };
GetSignature(foo);
}
void GetSignature(IGenerateSignature sig) {
Console.Write(sig.Generate());
}
I tried creating an intermediary struct like this:
struct FooSignaturizer : IGenerateSignature {
private readonly Foo _foo;
public FooSignaturizer(Foo f) {
_foo = f;
}
public static implicit operator FooSignaturizer(Foo f) {
return new FooSignaturizer(f);
}
public string Generate() {
return _foo.I + ":" + _foo.J;
}
}
But for some reason overload resolution fails to find the conversion from Foo to FooSignaturizer, and I get a "Cannot convert" compiler error. If I manually add a cast, GetSignature((FooSignaturizer) foo), it works. However, I need to also add support for the Bar and Qux types, with BarSignaturizer and QuxSignaturizer, so the cast won't work for those cases.
Is there a way to accomplish this?
As per 7.5.3.1 of the C# spec, only implicit conversions from argument expression to parameter type are considered.
7.5.3.1 Applicable function member
A function member is said to be an applicable function member with respect to an argument list A when all of the following are true:
Each argument in A corresponds to a parameter in the function member declaration as described in §7.5.1.1, and any parameter to which no argument corresponds is an optional parameter.
For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is identical to the parameter passing mode of the corresponding parameter, and
for a value parameter or a parameter array, an implicit conversion (§6.1) exists from the argument to the type of the corresponding parameter, or
for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter. After all, a ref or out parameter is an alias for the argument passed.
What you have here isn't an implicit conversion from Foo to IGenereateSignature, it's a wrapper.
As an explanation for this behaviour, you can't exect the compiler to go though every implementation of IGenerateSignature in scope to see whether it has an implicit conversion to/from Foo. What if there was more than one?
In terms of how you can achieve this for Foo, Bar and Qux...
What you're trying to achieve, one call to GetSignature(fooOrBarOrQux), isn't possible, because (based on your description of Foo) you can't have one variable that can be a Foo or a Bar or a Qux at compile time - they're unrelated. You'll always need three call sites, so there's no reason not to have three slightly-different conversions (wrapper class or overloaded method call or something) for the three cases.
... unless you use dynamic?
Rawling's answer gives a good explanation of why you have the problem. Since you can't fix this with implicit conversion, you could try extension methods to convert all types to IGenerateSignature like this:
void Test() {
var foo = new Foo { I = 1, J = 2 };
GetSignature(foo.AsIGenerateSignature());
}
void GetSignature(IGenerateSignature sig) {
Console.Write(sig.Generate());
}
public static class GenerateSignatureExtensions
{
public static IGenerateSignature AsIGenerateSignature(this IGenerateSignature me)
{
return me;
}
public static IGenerateSignature AsIGenerateSignature(this Foo me)
{
return new FooSignaturizer(me);
}
public static IGenerateSignature AsIGenerateSignature(this Bar me)
{
return new BarSignaturizer(me);
}
//....
}
Rawling's answer gives a great explanation of the why you are having a problem. As to how to achieve what you want. I might consider something like this:
public interface ISignaturizer
{
IGenerateSignature ToSignaturizer();
}
struct FooSignaturizer : IGenerateSignature, ISignaturizer{
private readonly Foo _foo;
public FooSignaturizer(Foo f) {
_foo = f;
}
public string Generate() {
return _foo.I + ":" + _foo.J;
}
public IGenerateSignature ToSignaturizer()
{
return (IGenerateSignature)this;
}
}
Now BarSignaturizer and QuxSignaturizer can implement the same interface. And then you can do:
GetSignature(((ISignaturizer)fooOrBarOrQux).ToSignaturizer());
Which isn't quite as elegant, but I think should accomplish what you need.

Is there a workaround to C# not being able to infer generic type arguments using type constraints?

Eric Lippert has explained in his blog post at http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx why constraints are not considered for type inference, which makes sense given that methods cannot be overloaded by simply changing type constraints. However, I would like to find a way to instantiate an object using two generic types, one which can be inferred and another which could be inferred if constraints were considered, without having to specify any of the types.
Given the types:
public interface I<T>
{
Other<T> CreateOther();
}
public class C : I<string>
{
public Other<string> CreateOther()
{
return new Other<string>();
}
}
public class Other<T>
{
}
and the factory:
public static class Factory1
{
public static Tuple<T, Other<T1>> Create<T, T1>(T o) where T : I<T1>
{
return new Tuple<T, Other<T1>>(o, o.CreateOther());
}
}
the following desired code will not compile:
public void WontCompile()
{
C c = new C();
var v = Factory1.Create(c); // won't compile
}
The error message is "error CS0411: The type arguments for method 'yo.Factory1.Create(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.", which is in line with what Eric said in his blog post.
Thus, we can simply specify the generic type arguments explicitly, as the error message suggests:
public void SpecifyAllTypes()
{
C c = new C();
var v = Factory1.Create<C, string>(c); // type is Tuple<C, Other<string>>
}
If we don't wish to specify type arguments and we don't need to retain type C, we can use the following factory:
public static class Factory2
{
public static Tuple<I<T1>, Other<T1>> CreateUntyped<T1>(I<T1> o)
{
return new Tuple<I<T1>, Other<T1>>(o, o.CreateOther());
}
}
and now specify:
public void Untyped()
{
C c = new C();
var v = Factory2.CreateUntyped(c); // type is Tuple<I<string>, Other<string>>
}
However, I wish to retain type C in the returned object and not specify the types.
I came up with a solution to this problem, but it seems to be a kludge of a workaround, where the object of type C is used twice in a two-step factory call.
To do this, the following factories are used:
public static class Factory3
{
public static Factory<T1> CreateFactory<T1>(I<T1> o)
{
return new Factory<T1>();
}
}
public class Factory<T1>
{
public Tuple<T, Other<T1>> Create<T>(T o) where T : I<T1>
{
return new Tuple<T, Other<T1>>(o, o.CreateOther());
}
}
which can then be used as follows:
public void Inferred()
{
C c = new C();
var v = Factory3.CreateFactory(c).Create(c); // type is Tuple<C, Other<string>>
}
This just feels odd since c is used twice. The first time it is used it is actually discarded as it is just being used to infer the base type argument.
Is there a better solution to this problem where the object does not need to be used twice and the types do not need to be specified?
edit: I just realized that, although the object must be used twice, the second factory class is not needed. Rather, both parameters could just be used in the same factory method as follows:
public class Factory
{
public Tuple<T, Other<T1>> Create<T, T1>(T o, I<T1> o2) where T : I<T1>
{
return new Tuple<T, Other<T1>>(o, o.CreateOther());
}
}
This would be used as follows:
public void Inferred()
{
C c = new C();
var v = Factory.Create(c, c); // type is Tuple<C, Other<string>>
}
It's still not ideal, but better than having to create a second factory class, and at least XMLDoc comments could be used to indicate that both parameters should be the same object. Once again, the one parameter (o2 in this case) is only used to infer the constrained types for T.

Conversion of IEnumerable<T> for Extension Method Issue

I have the following class and extension class (for this example):
public class Person<T>
{
public T Value { get; set; }
}
public static class PersonExt
{
public static void Process<TResult>(this Person<IEnumerable<TResult>> p)
{
// Do something with .Any().
Console.WriteLine(p.Value.Any());
}
}
I was expecting I could write something like the following and it would work, but it doesn't:
var x = new Person<List<String>>();
x.Process();
Since List is lower in the inheritance tree than IEnumerable, shouldn't this be valid? It works if I new up a Person<IEnumerable<String>> of course because that's the direct type.
I'm trying to use an extension method that can be applied to all Person<T>'s as long as T implements IEnumerable<Something> because I need to use the .Any() method.
EDIT: Maybe my understanding of covariance is off? I know IEnumerable<String> should convert to IEnumerable<Object>, but couldn't IList<String> convert to IEnumerable<String>?
EDIT2: Forgot to mention that I am using .net 4.0.
I know IEnumerable<String> should
convert to IEnumerable<Object>, but
couldn't IList<String> convert to
IEnumerable<String>?
IList<String> can convert to IEnumerable<String>. The problem is that you're trying to convert Person<List<String>> to Person<IEnumerable<String>>, which is illegal. For example, it's perfectly valid to write:
var x = new Person<IEnumerable<String>>();
x.Value = new string[0];
since Value is of type IEnumerable<String> and a string array is an IEnumerable<String>. However, you cannot write:
var x = new Person<List<String>>();
x.Value = new string[0];
since Value is of type List<String>. Since you can't use a Person<List<String>> in all places where you could use a Person<IEnumerable<String>>, it's not a legal cast.
Note that you can do something similar to what you want if you add a second type parameter to your extension method:
public static void Process<TResult, TList>(this Person<TList> p)
where TList : IEnumerable<TResult>
{
Console.WriteLine(p.Value.Any());
}
Unfortunately, the compiler won't be able to infer both type parameters, so you would have to call it like this:
var x = new Person<List<String>>();
x.Process<String, List<String>>();
If you are using C# 4.0 and can use covariance, then you can define a covariant interface for person:
public interface IPerson<out T>
{
T Value { get; }
}
public class Person<T>
: IPerson<T>
{
public T Value { get; set; }
}
And then write your extension method as:
public static void Process<TResult>(this IPerson<IEnumerable<TResult>> p)
{
// Do something with .Any().
Console.WriteLine(p.Value.Any());
}
Since IPerson<T>.Value is read-only, a IPerson<List<String>> can be used everywhere that an IPerson<IEnumerable<String>> can be, and the conversion is valid.
I'm not sure you've quite grasped the correct use of generics. In any event ...
The only thing that is incorrect is your declaration of extension method, and the way you are attempting to constrain the extension method.
public static class ThingExtensions
{
public static void Process<T>(this Thing<T> p)
where T : IEnumerable<string>
{
// Do something with .Any().
Console.WriteLine(p.Value.Any());
}
}
All I've really done is rename Person to Thing so that we're not getting hung up on what a Person<List<string>> really is.
public class Thing<T>
{
public T Value { get; set; }
}
class ListOfString : List<string>
{ }
class Program
{
static void Main(string[] args)
{
var x = new Thing<ListOfString>();
x.Value = new ListOfString();
x.Process();
x.Value.Add("asd");
x.Process();
var x2 = new Thing<int>();
// Error 1 The type 'int' cannot be used as type parameter 'T'
// in the generic type or method
// 'ThingExtensions.Process<T>(Thing<T>)'.
// There is no boxing conversion from 'int' to
// 'System.Collections.Generic.IEnumerable<string>'.
//x2.Process();
Console.Read();
}
}
You could also move the generic constraint to the Thing<T> if that was more applicable.
You mention covariance, but don't actually use it. You have to specify in or out on your generic parameters. Note that co/contravariance doesn't work on class types; they must be applied to interfaces.
So, introducing an interface and making it covariant:
public interface IPerson<out T>
{
T Value { get; }
}
public class Person<T> : IPerson<T>
{
public T Value { get; set; }
}
public static class PersonExt
{
public static void Process<TResult>(this IPerson<IEnumerable<TResult>> p)
{
// Do something with .Any().
Console.WriteLine(p.Value.Any());
}
}
allows this code to compile:
var x = new Person<List<String>>();
x.Process();

Categories