Supongamos que escribo una biblioteca con lo siguiente:¿Es posible especificar una restricción genérica para que un parámetro de tipo sea convertible DE OTRO tipo?
public class Bar { /* ... */ }
public class SomeWeirdClass<T>
where T : ???
{
public T BarMaker(Bar b)
{
// ... play with b
T t = (T)b
return (T) b;
}
}
Más tarde, espero que los usuarios utilizar mi biblioteca mediante la definición de sus propios tipos que son convertibles a Bar y utilizando el SomeWeirdClass 'fábrica'.
public class Foo
{
public static explicit operator Foo(Bar f)
{
return new Bar();
}
}
public class Demo
{
public static void demo()
{
Bar b = new Bar();
SomeWeirdClass<Foo> weird = new SomeWeirdClass<Foo>();
Foo f = weird.BarMaker(b);
}
}
este compilará si fijo where T : Foo
pero el problema es que yo no sé nada de Foo en tiempo de compilación de la biblioteca, y en realidad quiero algo más como where T : some class that can be instantiated, given a Bar
Es esto posible? De mi limitado conocimiento, no parece ser así, pero el ingenio de .NET Framework y sus usuarios siempre me sorprende ...
Esto puede o no estar relacionado con la idea de static interface methods - al menos, puedo ver el valor de ser capaz de especificar la presencia de métodos de fábrica para crear objetos (similar a la de la misma manera que ya se puede realizar where T : new()
)
edición: Solución - gracias a Nick y bzIm - para otros lectores voy proporcionar una solución completa según la entiendo: edición2: esta solución requiere que Foo exponga un constructor predeterminado público. Para una solución incluso
stupider
mejor que no requiere esto, vea la parte inferior de esta publicación.
public class Bar {}
public class SomeWeirdClass<T>
where T : IConvertibleFromBar<T>, new()
{
public T BarMaker(Bar b)
{
T t = new T();
t.Convert(b);
return t;
}
}
public interface IConvertibleFromBar<T>
{
T Convert(Bar b);
}
public class Foo : IConvertibleFromBar<Foo>
{
public static explicit operator Foo(Bar f)
{
return null;
}
public Foo Convert(Bar b)
{
return (Foo) b;
}
}
public class Demo
{
public static void demo()
{
Bar b = new Bar();
SomeWeirdClass<Foo> weird = new SomeWeirdClass<Foo>();
Foo f = weird.BarMaker(b);
}
}
Edit2: Solución 2: Crear una fábrica de convertidor Tipo de empleo:
#region library defined code
public class Bar {}
public class SomeWeirdClass<T, TFactory>
where TFactory : IConvertorFactory<Bar, T>, new()
{
private static TFactory convertor = new TFactory();
public T BarMaker(Bar b)
{
return convertor.Convert(b);
}
}
public interface IConvertorFactory<TFrom, TTo>
{
TTo Convert(TFrom from);
}
#endregion
#region user defined code
public class BarToFooConvertor : IConvertorFactory<Bar, Foo>
{
public Foo Convert(Bar from)
{
return (Foo) from;
}
}
public class Foo
{
public Foo(int a) {}
public static explicit operator Foo(Bar f)
{
return null;
}
public Foo Convert(Bar b)
{
return (Foo) b;
}
}
#endregion
public class Demo
{
public static void demo()
{
Bar b = new Bar();
SomeWeirdClass<Foo, BarToFooConvertor> weird = new SomeWeirdClass<Foo, BarToFooConvertor>();
Foo f = weird.BarMaker(b);
}
}
¿No es el objetivo de crear API para que sean * fáciles * de usar? –
Considérelo una pregunta pedagógica interesante. – fostandy