Dado el siguiente programa:El uso dinámico para establecer las propiedades dispares de no controlada (tercero) sellaron tipos
using System;
using System.Collections.Generic;
namespace ConsoleApplication49
{
using FooSpace;
class Program
{
static void Main(string[] args)
{
IEnumerable<FooBase> foos = FooFactory.CreateFoos();
foreach (var foo in foos)
{
HandleFoo(foo);
}
}
private static void HandleFoo(FooBase foo)
{
dynamic fooObject = foo;
ApplyFooDefaults(fooObject);
}
private static void ApplyFooDefaults(Foo1 foo1)
{
foo1.Name = "Foo 1";
Console.WriteLine(foo1);
}
private static void ApplyFooDefaults(Foo2 foo2)
{
foo2.Name = "Foo 2";
foo2.Description = "SomeDefaultDescription";
Console.WriteLine(foo2);
}
private static void ApplyFooDefaults(Foo3 foo3)
{
foo3.Name = "Foo 3";
foo3.MaxSize = Int32.MaxValue;
Console.WriteLine(foo3);
}
private static void ApplyFooDefaults(Foo4 foo4)
{
foo4.Name = "Foo 4";
foo4.MaxSize = 99999999;
foo4.EnableCache = true;
Console.WriteLine(foo4);
}
private static void ApplyFooDefaults(FooBase unhandledFoo)
{
unhandledFoo.Name = "Unhandled Foo";
Console.WriteLine(unhandledFoo);
}
}
}
/////////////////////////////////////////////////////////
// Assume this namespace comes from a different assembly
namespace FooSpace
{
////////////////////////////////////////////////
// these cannot be changed, assume these are
// from the .Net framework or some 3rd party
// vendor outside of your ability to alter, in
// another assembly with the only way to create
// the objects is via the FooFactory and you
// don't know which foos are going to be created
// due to configuration.
public static class FooFactory
{
public static IEnumerable<FooBase> CreateFoos()
{
List<FooBase> foos = new List<FooBase>();
foos.Add(new Foo1());
foos.Add(new Foo2());
foos.Add(new Foo3());
foos.Add(new Foo4());
foos.Add(new Foo5());
return foos;
}
}
public class FooBase
{
protected FooBase() { }
public string Name { get; set; }
public override string ToString()
{
return String.Format("Type = {0}, Name=\"{1}\"", this.GetType().FullName, this.Name);
}
}
public sealed class Foo1 : FooBase
{
internal Foo1() { }
}
public sealed class Foo2 : FooBase
{
internal Foo2() { }
public string Description { get; set; }
public override string ToString()
{
string baseString = base.ToString();
return String.Format("{0}, Description=\"{1}\"", baseString, this.Description);
}
}
public sealed class Foo3 : FooBase
{
internal Foo3() { }
public int MaxSize { get; set; }
public override string ToString()
{
string baseString = base.ToString();
return String.Format("{0}, MaxSize={1}", baseString, this.MaxSize);
}
}
public sealed class Foo4 : FooBase
{
internal Foo4() { }
public int MaxSize { get; set; }
public bool EnableCache { get; set; }
public override string ToString()
{
string baseString = base.ToString();
return String.Format("{0}, MaxSize={1}, EnableCache={2}", baseString,
this.MaxSize,
this.EnableCache);
}
}
public sealed class Foo5 : FooBase
{
internal Foo5() { }
}
////////////////////////////////////////////////
}
que produce el siguiente resultado:
Type = ConsoleApplication49.Foo1, Name="Foo 1"
Type = ConsoleApplication49.Foo2, Name="Foo 2", Description="SomeDefaultDescription"
Type = ConsoleApplication49.Foo3, Name="Foo 3", MaxSize=2147483647
Type = ConsoleApplication49.Foo4, Name="Foo 4", MaxSize=99999999, EnableCache=True
Type = ConsoleApplication49.Foo5, Name="Unhandled Foo"
Press any key to continue . . .
opté por usar dinámico aquí para evitar el siguiente:
- usando las declaraciones switch/if/else eg
switch(foo.GetType().Name)
- declaraciones de comprobación de tipo explícitas, p.
foo is Foo1
- declaraciones explícitas de fundición, p.
(Foo1)foo
Debido a la conversión dynamic
, el método correcto ApplyFooDefaults
se invoca basándose en el tipo del objeto pasado a HandleFoo(FooBase foo)
. Cualquier objeto que no tenga un método de controlador ApplyFooDefaults
apropiado, cae en el método "catch all", ApplyFooDefaults(FooBase unhandledFoo)
.
Una parte clave aquí es que FooBase y las clases derivadas representan tipos que están fuera de nuestro control y de los que no se puede derivar para agregar interfaces adicionales.
¿Es este un "buen" uso para dinámico o puede resolverse este problema de forma OOP sin agregar complejidad adicional dadas las restricciones y el hecho de que esto es solo para establecer valores de propiedad predeterminados en estos objetos?
* ACTUALIZADO *
Después de la respuesta de Bob Horn, me di cuenta de que mi situación no era completa. Restricciones adicionales:
- No puede crear los Foos directamente, tiene que usar FooFactory.
- No puede suponer el tipo Foo porque el tipo Foo se especifica en la configuración y se creó de forma reflexiva.
.
@Jon Skeet - Lo oí hablar en Code Mash 2012 en Sandusky, OH. En su mayor parte, parecía que no le interesaba el uso de tipos dinámicos en ninguna situación. Me encantaría tener tus pensamientos sobre esta pregunta. –
Creo que su código es muy, * muy * bueno como es. – Alex
Creo que es posible que desee considerar el uso de genéricos si está configurado con el uso dinámico. Similar: http://stackoverflow.com/q/10132760/1026459. Nota: Jon Skeet proporciona la respuesta en este enlace. –