Obtenir une nouvelle instance d'objet à partir d'un type

Il se peut que vous ne connaissiez pas toujours le type d'un objet lors de la compilation, mais que vous deviez créer une instance du type. Comment obtenez-vous une nouvelle instance d'objet à partir d'un type?

0
ajouté édité
Vues: 7
Parlez-vous du Couche de base dans ArcGIS 10?
ajouté l'auteur Devdatta Tengshe, source
Je pense qu'il parle de l'implémentation d'un calque personnalisé en utilisant la classe BaseCustomLayer.
ajouté l'auteur Reed Copsey, source

13 Réponses

Avez-vous essayé de BaseCustomGlobeLayer à la place?

Update: I just tried that and it doesn't work. I then derived from BaseCustomLayer then implemented IGraphicsContainer3D and was able to add it to the scene without getting an error.

2
ajouté
Question de suivi créée ici gis.stackexchange.com/questions/18301/…
ajouté l'auteur saint_groceon, source
En parcourant les coclasses qui implémentent ILayerEvents il semble que tous le font en tant qu'interface sortante (sauf pour les couches 3D et 3D). Une bonne question serait: Comment implémenter plusieurs interfaces sortantes pour les classes personnalisées en C #?
ajouté l'auteur saint_groceon, source
Je pense que c'est un autre problème lié à une interface sortante manquante. Dans arcmap, la Coclasse de la couche supérieure implémente ILayerEvents en tant qu'interface sortante. Je suspecte la documentation pour graphicslayer3d est incorrect et que ILayerEvents doit êt
ajouté l'auteur saint_groceon, source
@ReedCopsey En regardant l'OMD je vois que GraphicsLayer3D implémente IActiveViewEvents en tant qu'interface sortante. Je suspecte que ItemDeleted doit se déclencher lorsque les graphiques sont supprimés.
ajouté l'auteur saint_groceon, source
Je n'étais pas sûr, jusqu'à ce que j'ai essayé. Vous avez raison, cela ne fonctionne pas avec ArcScene.
ajouté l'auteur saint_groceon, source
IGraphicsContainer3D semble être la clé. Merci!
ajouté l'auteur Thejesh GN, source
N'est-ce pas seulement pour ArcGlobe? Cela fonctionne-t-il également dans ArcScene?
ajouté l'auteur Reed Copsey, source
@DBaker Avez-vous eu ce travail à la fin? J'ai essayé ceci, et tandis qu'il ajoute sans erreur, le comportement n'est pas "propre" que ce soit (ie: les graphiques du IGraphicsContainer3D ne seront jamais supprimés, etc) ...
ajouté l'auteur Reed Copsey, source
@KirkKuykendall Je viens d'implémenter l'ensemble IActiveViewEvents (et ILayerEvents) et toujours obtenir le même comportement étrange ...
ajouté l'auteur Reed Copsey, source
Ce n'est pas que je supprime des graphiques - même le réglage de la couche Visibilité sur false ne provoque pas l'effacement des éléments.
ajouté l'auteur Reed Copsey, source

Une implémentation de ce problème consiste à tenter d'appeler le constructeur sans paramètre du Type:

public static object GetNewObject(Type t)
{
    try
    {
        return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return null;
    }
}

Voici la même approche, contenue dans une méthode générique:

public static T GetNewObject()
{
    try
    {
        return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return default(T);
    }
}
0
ajouté
Programmation axée sur les exceptions? Cela semble être une très mauvaise implémentation lorsque vous pouvez simplement réfléchir sur le type pour déterminer les constructeurs.
ajouté l'auteur Firoso, source

La classe Activator dans l'espace de noms racine System est assez puissante.

Il y a beaucoup de surcharges pour passer des paramètres au constructeur et autres. Consultez la documentation sur:

http://msdn.microsoft.com/fr us / library / system.activator.createinstance.aspx

ou (nouveau chemin)

https://docs.microsoft.com/fr us / dotnet / api / system.activator.createinstance

Voici quelques exemples simples:

ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType");
0
ajouté
Heureux d'avoir finalement trouvé ceci, mais le deuxième appel n'est pas exactement exact, manquant une citation et des pars inversés, devrait être: ObjectType instance = (ObjectType) Activator.CreateInstance ("MyAssembly", "MyNamespa & zwnj; ce.ObjectType");
ajouté l'auteur kevinc, source
Vous devez appeler 'Unwrap ()' pour obtenir le type réel d'objet que vous voulez: ConcreteType instance = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap ();
ajouté l'auteur Ε Г И І И О, source

Sans utiliser de réflexion:

private T Create() where T : class, new()
{
    return new T();
}
0
ajouté
Vous connaissez tous les types possibles au moment de la compilation, mais vous ne savez pas quelle implémentation sera utilisée lors de l'exécution.
ajouté l'auteur Robert P., source
Si vous travaillez avec des classes génériques et des interfaces dans des usines, les types qui implémentent l'interface doivent être instanciés peuvent varier.
ajouté l'auteur Robert P., source
un nouveau T (); échouerait si T n'est pas un type de référence avec un constructeur sans paramètre. Cette méthode utilise des contraintes pour s'assurer que T est un type de référence et possède un constructeur.
ajouté l'auteur Robert P., source
Donc, T peut varier à l'exécution. Utile si vous travaillez avec des types deríved.
ajouté l'auteur Robert P., source
@RobertP. Vous pouvez créer de nouveaux types lors de l'exécution. Il n'y a pas de règle qui dit que vous connaissez tous les types au moment de la compilation. Et non je ne parle pas de génériques. Vous pouvez créer des types complètement nouveaux et ajouter tous les champs, propriétés et méthodes, etc. à l'exécution. Il y a aussi le cas simple où vous voulez créer une instance d'un type qui est dans un assembly qui n'est pas connu au moment de la compilation. C'est plutôt commun.
ajouté l'auteur AnorZaken, source
Comment T peut-il varier à l'exécution? Vous n'avez pas besoin de connaître T au moment du design pour appeler Create <>?
ajouté l'auteur Kyle Delaney, source
Comment est-ce utile? Vous devez déjà connaître le type pour appeler cette méthode, et si vous connaissez le type, vous pouvez le construire sans méthode spéciale.
ajouté l'auteur Kyle Delaney, source

Je peux traverser cette question parce que je cherchais à implémenter une simple méthode CloneObject pour une classe arbitraire (avec un constructeur par défaut)

Avec la méthode générique, vous pouvez exiger que le type implémente New ().

Public Function CloneObject(Of T As New)(ByVal src As T) As T
    Dim result As T = Nothing
    Dim cloneable = TryCast(src, ICloneable)
    If cloneable IsNot Nothing Then
        result = cloneable.Clone()
    Else
        result = New T
        CopySimpleProperties(src, result, Nothing, "clone")
    End If
    Return result
End Function

Avec non-générique assumer le type a un constructeur par défaut et attraper une exception si ce n'est pas le cas.

Public Function CloneObject(ByVal src As Object) As Object
    Dim result As Object = Nothing
    Dim cloneable As ICloneable
    Try
        cloneable = TryCast(src, ICloneable)
        If cloneable IsNot Nothing Then
            result = cloneable.Clone()
        Else
            result = Activator.CreateInstance(src.GetType())
            CopySimpleProperties(src, result, Nothing, "clone")
        End If
    Catch ex As Exception
        Trace.WriteLine("!!! CloneObject(): " & ex.Message)
    End Try
    Return result
End Function
0
ajouté

Le code générique ne fonctionnerait-il pas?

0
ajouté
En fait, ce serait dans une classe / méthode générique, mais pas pour un "Type" donné.
ajouté l'auteur Brady Moritz, source
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

La classe Activator a une variante générique qui rend cela un peu plus facile:

ObjectType instance = Activator.CreateInstance();
0
ajouté
@Kevin Bien sûr. Une telle opération <?> Peut? T travailler dans un langage statiquement typé parce que cela n'a pas de sens. Vous ne pouvez pas appeler des méthodes sur un objet de type inconnu. En attendant (= depuis l'écriture de cette réponse), C# a la construction dynamic qui permet d'autoriser de telles constructions, mais pour la plupart des applications, cette réponse la couvre encore.
ajouté l'auteur Konrad Rudolph, source
@AnorZaken Mon commentaire ne dit rien sur la création de types lors de l'exécution. Bien sûr, vous pouvez le faire, mais vous ne pouvez pas les utiliser statiquement dans le même contexte (vous pouvez héberger un programme compilé statiquement, bien sûr). C'est tout ce que dit mon commentaire.
ajouté l'auteur Konrad Rudolph, source
Sauf que cela ne fonctionne pas pour l'exécution Type t .
ajouté l'auteur Kevin P. Rice, source
@KonradRudolph Ah désolé, mal interprété "une telle opération" à l'instanciation d'un type qui est seulement connu au moment de l'exécution; au lieu de signifier l'utilisation d'un type d'exécution en tant que paramètre de type générique.
ajouté l'auteur AnorZaken, source
@KonradRudolph Pas tout à fait vrai. Le premier de C# permet de créer de nouveaux types lors de l'exécution. Vous ne pouvez simplement rien appeler sur eux d'une manière statiquement sûre . Donc oui, vous êtes à moitié correct. Mais de façon plus réaliste, vous en avez besoin lorsque vous chargez des assemblages à l'exécution, ce qui signifie que le type n'est pas connu au moment de la compilation. C# serait sévèrement limité si vous ne pouviez pas faire cela. Je veux dire que vous venez de le prouver vous-même: comment fonctionne la méthode Activator qui prend une instanc
ajouté l'auteur AnorZaken, source

L'expression compilée est la meilleure façon! (pour que les performances créent à plusieurs reprises une instance en cours d'exécution).

static readonly Func YCreator = Expression.Lambda>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
 ).Compile();

X x = YCreator();

Statistiques (2012):

    Iterations: 5000000
    00:00:00.8481762, Activator.CreateInstance(string, string)
    00:00:00.8416930, Activator.CreateInstance(type)
    00:00:06.6236752, ConstructorInfo.Invoke
    00:00:00.1776255, Compiled expression
    00:00:00.0462197, new

Statistiques (2015, .net 4.5, x64):

    Iterations: 5000000
    00:00:00.2659981, Activator.CreateInstance(string, string)
    00:00:00.2603770, Activator.CreateInstance(type)
    00:00:00.7478936, ConstructorInfo.Invoke
    00:00:00.0700757, Compiled expression
    00:00:00.0286710, new

Statistiques (2015, .net 4.5, x86):

    Iterations: 5000000
    00:00:00.3541501, Activator.CreateInstance(string, string)
    00:00:00.3686861, Activator.CreateInstance(type)
    00:00:00.9492354, ConstructorInfo.Invoke
    00:00:00.0719072, Compiled expression
    00:00:00.0229387, new

Statistiques (2017, LINQPad 5.22.02 / x64 / .NET 4.6):

    Iterations: 5000000
    No args
    00:00:00.3897563, Activator.CreateInstance(string assemblyName, string typeName)
    00:00:00.3500748, Activator.CreateInstance(Type type)
    00:00:01.0100714, ConstructorInfo.Invoke
    00:00:00.1375767, Compiled expression
    00:00:00.1337920, Compiled expression (type)
    00:00:00.0593664, new
    Single arg
    00:00:03.9300630, Activator.CreateInstance(Type type)
    00:00:01.3881770, ConstructorInfo.Invoke
    00:00:00.1425534, Compiled expression
    00:00:00.0717409, new

Code complet:

static X CreateY_New()
{
    return new Y();
}

static X CreateY_New_Arg(int z)
{
    return new Y(z);
}

static X CreateY_CreateInstance()
{
    return (X)Activator.CreateInstance(typeof(Y));
}

static X CreateY_CreateInstance_String()
{
    return (X)Activator.CreateInstance("Program", "Y").Unwrap();
}

static X CreateY_CreateInstance_Arg(int z)
{
    return (X)Activator.CreateInstance(typeof(Y), new object[] { z, });
}

private static readonly System.Reflection.ConstructorInfo YConstructor =
    typeof(Y).GetConstructor(Type.EmptyTypes);
private static readonly object[] Empty = new object[] { };
static X CreateY_Invoke()
{
    return (X)YConstructor.Invoke(Empty);
}

private static readonly System.Reflection.ConstructorInfo YConstructor_Arg =
    typeof(Y).GetConstructor(new[] { typeof(int), });
static X CreateY_Invoke_Arg(int z)
{
    return (X)YConstructor_Arg.Invoke(new object[] { z, });
}

private static readonly Func YCreator = Expression.Lambda>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
static X CreateY_CompiledExpression()
{
    return YCreator();
}

private static readonly Func YCreator_Type = Expression.Lambda>(
   Expression.New(typeof(Y))
).Compile();
static X CreateY_CompiledExpression_Type()
{
    return YCreator_Type();
}

private static readonly ParameterExpression YCreator_Arg_Param = Expression.Parameter(typeof(int), "z");
private static readonly Func YCreator_Arg = Expression.Lambda>(
   Expression.New(typeof(Y).GetConstructor(new[] { typeof(int), }), new[] { YCreator_Arg_Param, }),
   YCreator_Arg_Param
).Compile();
static X CreateY_CompiledExpression_Arg(int z)
{
    return YCreator_Arg(z);
}

static void Main(string[] args)
{
    const int iterations = 5000000;

    Console.WriteLine("Iterations: {0}", iterations);

    Console.WriteLine("No args");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(string assemblyName, string typeName)", Creator = (Func)CreateY_CreateInstance},
        new {Name = "Activator.CreateInstance(Type type)", Creator = (Func)CreateY_CreateInstance},
        new {Name = "ConstructorInfo.Invoke", Creator = (Func)CreateY_Invoke},
        new {Name = "Compiled expression", Creator = (Func)CreateY_CompiledExpression},
        new {Name = "Compiled expression (type)", Creator = (Func)CreateY_CompiledExpression_Type},
        new {Name = "new", Creator = (Func)CreateY_New},
    })
    {
        var creator = creatorInfo.Creator;

        var sum = 0;
        for (var i = 0; i < 1000; i++)
            sum += creator().Z;

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; ++i)
        {
            var x = creator();
            sum += x.Z;
        }
        stopwatch.Stop();
        Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name);
    }

    Console.WriteLine("Single arg");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(Type type)", Creator = (Func)CreateY_CreateInstance_Arg},
        new {Name = "ConstructorInfo.Invoke", Creator = (Func)CreateY_Invoke_Arg},
        new {Name = "Compiled expression", Creator = (Func)CreateY_CompiledExpression_Arg},
        new {Name = "new", Creator = (Func)CreateY_New_Arg},
    })
    {
        var creator = creatorInfo.Creator;

        var sum = 0;
        for (var i = 0; i < 1000; i++)
            sum += creator(i).Z;

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; ++i)
        {
            var x = creator(i);
            sum += x.Z;
        }
        stopwatch.Stop();
        Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name);
    }
}

public class X
{
  public X() { }
  public X(int z) { this.Z = z; }
  public int Z;
}

public class Y : X
{
    public Y() {}
    public Y(int z) : base(z) {}
}
0
ajouté
Exemple peut-être?
ajouté l'auteur ajeh, source
Est-ce toujours utile quand vous ne savez pas quel type X est en cours d'exécution?
ajouté l'auteur ajeh, source
il y a aussi TypeDescriptor.CreateInstance (voir stackoverflow.com/a/17797389/1242 ) qui peut être plus rapide si elle est utilisée avec TypeDescriptor .AddProvider
ajouté l'auteur Lars Truijens, source
@ Serj-Tm Non, cela ne fonctionnera pas si le type X est un runtime Type .
ajouté l'auteur NetMage, source
@ajeh Oui. Remplacez typeof (T) par Type.GetType (..).
ajouté l'auteur Serj-Tm, source
+1 pour toutes les statistiques! Je n'ai pas vraiment besoin de ce genre de performance pour le moment, mais c'est quand même très intéressant. :)
ajouté l'auteur AnorZaken, source

C'est assez simple. Supposons que votre nom de classe est Car et que l'espace de nom est Vehicles , passez le paramètre Vehicles.Car qui renvoie l'objet de type Car . Comme ceci, vous pouvez créer n'importe quelle instance de n'importe quelle classe dynamiquement.

public object GetInstance(string strNamesapace)
{         
     Type t = Type.GetType(strNamesapace); 
     return  Activator.CreateInstance(t);         
}

Si votre nom complet (c.-à-d. , Vehicles.Car dans ce cas) est dans un autre assembly, le Type.GetType sera null. Dans ce cas, vous parcourez tous les assemblages et trouvez le Type . Pour cela, vous pouvez utiliser le code ci-dessous

public object GetInstance(string strFullyQualifiedName)
{
     Type type = Type.GetType(strFullyQualifiedName);
     if (type != null)
         return Activator.CreateInstance(type);
     foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
     {
         type = asm.GetType(strFullyQualifiedName);
         if (type != null)
             return Activator.CreateInstance(type);
     }
     return null;
 }

Et vous pouvez obtenir l'instance en appelant la méthode ci-dessus.

object objClassInstance = GetInstance("Vehicles.Car");
0
ajouté
@danmiser Cela nécessite de coder en dur le nom de l'assembly. Afin de mettre en œuvre la flexibilité, je vérifie null et le code fonctionne de manière dynamique :)
ajouté l'auteur Sarath Avanavu, source
Dans votre deuxième cas (assemblage externe), vous pouvez simplement passer dans "Véhicules.Voiture, AutreAssemblage" à votre première méthode et cela fonctionnera. Evidemment OtherAssembly est le nom de l'assemblage dans lequel il vit.
ajouté l'auteur danmiser, source

Si c'est pour quelque chose qui sera appelé beaucoup dans une instance d'application, il est beaucoup plus rapide de compiler et de mettre en cache le code dynamique au lieu d'utiliser l'activateur ou ConstructorInfo.Invoke() . Deux options faciles pour la compilation dynamique sont compilées Linq Expressions ou quelques simples IL opcodes et DynamicMethod . De toute façon, la différence est énorme quand vous commencez à entrer dans des boucles serrées ou des appels multiples.

0
ajouté
Le lien "IL opcodes and DynamicMethod" est mort.
ajouté l'auteur Judge Bread, source
public AbstractType New
{
    get
    {
        return (AbstractType) Activator.CreateInstance(GetType());
    }
}
0
ajouté

Si vous voulez utiliser le constructeur par défaut, alors la solution utilisant System.Activator présentée plus tôt est probablement la plus pratique. Cependant, si le type manque d'un constructeur par défaut ou si vous devez utiliser un constructeur par défaut, alors une option est d'utiliser la réflexion ou System.ComponentModel.TypeDescriptor . En cas de réflexion, il suffit de connaître juste le nom du type (avec son espace de nommage).

Exemple utilisant la réflexion:

ObjectType instance = 
    (ObjectType)System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(
        typeName: objectType.FulName, // string including namespace of the type
        ignoreCase: false,
        bindingAttr: BindingFlags.Default,
        binder: null,  // use default binder
        args: new object[] { args, to, constructor },
        culture: null, // use CultureInfo from current thread
        activationAttributes: null
    );

Exemple utilisant TypeDescriptor :

ObjectType instance = 
    (ObjectType)System.ComponentModel.TypeDescriptor.CreateInstance(
        provider: null, // use standard type description provider, which uses reflection
        objectType: objectType,
        argTypes: new Type[] { types, of, args },
        args: new object[] { args, to, constructor }
    );
0
ajouté

Étant donné ce problème, l'activateur fonctionnera s'il y a un ctor sans paramètre. Si c'est une contrainte, pensez à utiliser

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()
0
ajouté