Le code ci-dessous fonctionne bien tant que j'ai la classe ClassSameAssembly
dans le même assembly que la classe Program
. Mais lorsque je déplace la classe ClassSameAssembly
vers un assembly séparé, un RuntimeBinderException
(voir ci-dessous) est lancé. Est-il possible de le résoudre?
using System;
namespace ConsoleApplication2
{
public static class ClassSameAssembly
{
public static dynamic GetValues()
{
return new
{
Name = "Michael", Age = 20
};
}
}
internal class Program
{
private static void Main(string[] args)
{
var d = ClassSameAssembly.GetValues();
Console.WriteLine("{0} is {1} years old", d.Name, d.Age);
}
}
}
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
: 'objet' ne contient pas de définition pour 'Nom'
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at ConsoleApplication2.Program.Main(String[] args) in C:\temp\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 23
dynamic
c#-4.0
anonymous-types
mehanik
la source
la source
Réponses:
Je crois que le problème est que le type anonyme est généré en tant que
internal
, de sorte que le classeur ne le "sait" pas vraiment en tant que tel.Essayez plutôt d'utiliser ExpandoObject:
public static dynamic GetValues() { dynamic expando = new ExpandoObject(); expando.Name = "Michael"; expando.Age = 20; return expando; }
Je sais que c'est un peu moche, mais c'est le meilleur auquel je puisse penser pour le moment ... Je ne pense pas que vous puissiez même utiliser un initialiseur d'objet avec, car bien qu'il soit fortement typé car
ExpandoObject
le compilateur ne saura pas quoi faire avec "Nom" et "Âge". Vous pourrez peut- être faire ceci:dynamic expando = new ExpandoObject() { { "Name", "Michael" }, { "Age", 20 } }; return expando;
mais ce n'est pas beaucoup mieux ...
Vous pourriez potentiellement écrire une méthode d'extension pour convertir un type anonyme en un expando avec le même contenu via la réflexion. Ensuite, vous pourriez écrire:
return new { Name = "Michael", Age = 20 }.ToExpando();
C'est assez horrible cependant :(
la source
return Build<ExpandoObject>.NewObject(Name:"Micheal", Age: 20);
object
ou sur un type générique (vous pouvez exiger que ce soit une classe ...) et vérifier le type au moment de l'exécution.Vous pouvez utiliser
[assembly: InternalsVisibleTo("YourAssemblyName")]
pour rendre visibles les composants internes de votre assemblage.la source
J'ai rencontré un problème similaire et je voudrais ajouter à la réponse de Jon Skeets qu'il existe une autre option. La raison pour laquelle j'ai découvert est que j'ai réalisé que de nombreuses méthodes d'extension dans Asp MVC3 utilisent des classes anonymes comme entrée pour fournir des attributs html (new {alt = "Image alt", style = "padding-top: 5px"} =>
Quoi qu'il en soit - ces fonctions utilisent le constructeur de la classe RouteValueDictionary. J'ai essayé moi-même, et bien sûr cela fonctionne - bien que seulement le premier niveau (j'ai utilisé une structure à plusieurs niveaux). SO - dans le code, ce serait:
object o = new { name = "theName", props = new { p1 = "prop1", p2 = "prop2" } } SeparateAssembly.TextFunc(o) //In SeparateAssembly: public void TextFunc(Object o) { var rvd = new RouteValueDictionary(o); //Does not work: Console.WriteLine(o.name); Console.WriteLine(o.props.p1); //DOES work! Console.WriteLine(rvd["name"]); //Does not work Console.WriteLine(rvd["props"].p1); Console.WriteLine(rvd["props"]["p1"]);
SO ... Que se passe-t-il vraiment ici? Un coup d'œil à l'intérieur du RouteValueDictionary révèle ce code (valeurs ~ = o ci-dessus):
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values)) object obj2 = descriptor.GetValue(values); //"this.Add" would of course need to be adapted this.Add(descriptor.Name, obj2); }
SO - en utilisant TypeDescriptor.GetProperties (o), nous pourrions obtenir les propriétés et les valeurs malgré le type anonyme étant construit comme interne dans un assembly séparé! Et bien sûr, ce serait assez facile à étendre pour le rendre récursif. Et pour faire une méthode d'extension si vous le vouliez.
J'espère que cela t'aides!
/Victor
la source
Voici une version rudimentaire d'une méthode d'extension pour ToExpandoObject qui, j'en suis sûr, a de la place pour le polissage.
public static ExpandoObject ToExpandoObject(this object value) { // Throw is a helper in my project, replace with your own check(s) Throw<ArgumentNullException>.If(value, Predicates.IsNull, "value"); var obj = new ExpandoObject() as IDictionary<string, object>; foreach (var property in value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { obj.Add(property.Name, property.GetValue(value, null)); } return obj as ExpandoObject; } [TestCase(1, "str", 10.75, 9.000989, true)] public void ToExpandoObjectTests(int int1, string str1, decimal dec1, double dbl1, bool bl1) { DateTime now = DateTime.Now; dynamic value = new {Int = int1, String = str1, Decimal = dec1, Double = dbl1, Bool = bl1, Now = now}.ToExpandoObject(); Assert.AreEqual(int1, value.Int); Assert.AreEqual(str1, value.String); Assert.AreEqual(dec1, value.Decimal); Assert.AreEqual(dbl1, value.Double); Assert.AreEqual(bl1, value.Bool); Assert.AreEqual(now, value.Now); }
la source
Une solution plus propre serait:
var d = ClassSameAssembly.GetValues().ToDynamic();
Qui est maintenant un ExpandoObject.
N'oubliez pas de mentionner:
la source
La solution ci-dessous a fonctionné pour moi dans mes projets d'application console
Placez cet [assembly: InternalsVisibleTo ("YourAssemblyName")] dans \ Properties \ AssemblyInfo.cs du projet séparé avec la fonction renvoyant un objet dynamique.
"YourAssemblyName" est le nom d'assembly du projet appelant. Vous pouvez l'obtenir via Assembly.GetExecutingAssembly (). FullName en l'exécutant en appelant project.
la source
Méthode d'extension ToExpando (mentionnée dans la réponse de Jon) pour les courageux
public static class ExtensionMethods { public static ExpandoObject ToExpando(this object obj) { IDictionary<string, object> expando = new ExpandoObject(); foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(obj)) { var value = propertyDescriptor.GetValue(obj); expando.Add(propertyDescriptor.Name, value == null || new[] { typeof (Enum), typeof (String), typeof (Char), typeof (Guid), typeof (Boolean), typeof (Byte), typeof (Int16), typeof (Int32), typeof (Int64), typeof (Single), typeof (Double), typeof (Decimal), typeof (SByte), typeof (UInt16), typeof (UInt32), typeof (UInt64), typeof (DateTime), typeof (DateTimeOffset), typeof (TimeSpan), }.Any(oo => oo.IsInstanceOfType(value)) ? value : value.ToExpando()); } return (ExpandoObject)expando; } }
la source
Si vous utilisez déjà Newtonsoft.Json dans votre projet (ou si vous êtes prêt à l'ajouter à cette fin), vous pouvez implémenter cette horrible méthode d'extension à laquelle Jon Skeet fait référence dans sa réponse comme ceci:
public static class ObjectExtensions { public static ExpandoObject ToExpando(this object obj) => JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(obj)); }
la source