2009-07-27 9 views
0

J'ai une question sur le résultat de la requête LINQ et Lambda. Par exemple, je les codes suivants:Question sur LINQ et Lambda requery

class ClassA<T> { 
    public string Name { get; set; } 
    public T ObjectT { get; set; } 
} 

List<ClassA<T>> list; 
// list is populated 
// First way to get instance from list, reference type? 
ClassA<T> instance1 = list.Select(x=> x).Where(x=>x.Name == "A"). 
    FirstOrDefault(); 
// Second way to clone or copy instance from the list 
ClassA<T> instance2 = list.Select(x=> 
    new ClassA<T> { Name = x.Name, ObjectT = x.ObjectT}). 
    Where(x=> x.Name = "A").FirstOrDefault(); 

Il est évident que instance2 est un clone ou une copie d'une instance trouvée dans la liste. Que diriez-vous d'instance1? S'agit-il d'une nouvelle instance ou simplement d'une référence à une instance de la liste? Si instance1 est une référence d'objet à l'élément de la liste, toute modification de sa propriété peut changer le même objet dans la liste. Est-ce correct?

Si c'est le cas et que je ne veux pas avoir d'effet implicite sur les objets de la liste, je pense que je devrais utiliser la seconde stratégie. Cependant, si je veux que les changements dans les instances récupérées aient aussi les mêmes changements dans la liste, je devrais utiliser la stratégie 1. Je ne sais pas si ma compréhension est correcte. Des commentaires?

Répondre

1

Depuis votre ClassA<T> est un class (pas struct), instance1 est une référence au même objet qui est à l'intérieur du list.

A propos de la stratégie, si vous ne voulez pas que les utilisateurs de votre liste pour pouvoir mess des éléments de la liste, je suggère ces alternatives:

  • ClassA<T> un struct gagnez de. De cette façon, n'importe quelle requête retournera la copie des éléments dans la liste;

  • Faites ClassA<T> implémentez IClonable comme ci-dessous, et clonez-le avant de passer à un code non fiable.

Notez que la propriété ObjectT peut être une classe. Ainsi, même après avoir cloné l'objet ClassA<T>, la propriété ObjectT fera référence au même objet et tout code utilisateur pourra le modifier. Peut-être, vous aurez besoin de cloner ObjectT aussi;


class ClassA<T> : ICloneable 
{ 
    public string Name { get; set; } 
    public T ObjectT { get; set; } 

    public ClassA<T> Clone() 
    { 
     return (ClassA<T>)this.MemberwiseClone(); 
    } 

    object ICloneable.Clone() 
    { 
     return this.Clone(); 
    } 
} 

Comme suggéré, vous pouvez utiliser une méthode d'extension pour éviter les problèmes de référence null. Comme vous pouvez le voir, il existe plusieurs solutions au problème. Vous devriez choisir celui qui correspond le mieux à votre problème spécifique.

static class CloneableExt 
{ 
    public static T CloneNull<T>(this T obj) where T : class, ICloneable 
    { 
     if (obj == null) return null; 
     return (T)obj.Clone(); 
    } 
} 

EDIT 1: CloneableExt ajouté

+0

Je pense que cette façon serait recommandée, j'ai laissé tomber et changé en méthode d'extension pour propager les nulls. – Jimmy

+0

En effet, la méthode d'extension a cet avantage. En fait, vous pourriez même faire une extension à n'importe quelle classe 'ICloneable'. – jpbochi

1

Oui, la première requête ne clone pas les objets dans la liste, donc instance référencera l'objet réel de list. La seconde requête construit explicitement un clone de l'objet.

+0

mais une copie superficielle, non? l'ObjectT sera toujours une référence de l'instance d'origine .. –

+0

Oui, bien sûr. –

2

ce sera une référence à l'objet dans la liste. Qu'est-ce que vous pourriez faire, est de créer une copie à l'aide d'une fonction clone() qui retourne un double résultat dans le code

list.Where(x => x.Name == "A").FirstOrDefault().Clone() 

Notez que le produit (x => x) est inutile. Clone serait une méthode d'extension le long des lignes de

public static ClassA<T> Clone<T>(this ClassA<T> self) { 
    if (self == null) return null; 
    return new ClassA { 
     Name = self.Name, 
     ObjectT = self.ObjectT 
    } 
} 
+0

Merci pour votre version simplifiée. Cependant, pour moi, c'est vraiment difficile à lire. Où la clause le rend plus explicite et facile à lire. Cependant, si les codes après x => non-as-instance par défaut signifient x type de retour, c'est cool. Je ne sais pas quel chemin prendre. –

+0

ouais je l'ai changé, parce que FirstOrDefault ne supporte pas cette surcharge de toute façon. – Jimmy

+0

ne va pas juste créer une copie superficielle? ObjectT ne sera toujours qu'une référence .. –

0

Ce que vous faites dans votre deuxième exemple est une copie superficielle, qui est une nouvelle instance, mais avec ses membres de type refernce encore refensced.

Pour le faire correctement, vous devez mettre en œuvre ICloneable:

class ClassA<T> : System.ICloneable where T : System.ICloneable 
    { 
     public string Name { get; set; } 
     public T ObjT { get; set; } 


     public object Clone() 
     { 
      var clone = new ClassA<T>(); 
      clone.Name = (string)Name.Clone(); 
      clone.ObjT = (T)ObjT.Clone(); 
      return clone; 
     } 
    } 

puis

ClassA<T> instance2 = list.Where(x=> x.Name == "A").First().Clone();