Question

Est-ce que je fais quelque chose de mal ou est-il impossible de spécifier une classe générique en tant que contrainte pour une méthode générique?

Je me suis amusé avec les génériques et la db4o (base de données d'objets open source) et j'écris actuellement un test. programme (voir le code ci-dessous) pour stocker et récupérer des collections génériques définies par l'utilisateur.

J'essaie d'écrire une méthode générique (voir GetCollectionFromDb ci-dessous) pour extraire une collection spécifiquement typée de la base de données. Malheureusement, le code ci-dessous renvoie une erreur générée par le compilateur pour la ligne:

 MyCollection1 collection3 =
                  GetCollectionFromDb<MyCollection1>(Collection1Name);

Le message d'erreur est le suivant:

The type 'GenericsTest.MyCollection1'cannot be used as type parameter 'T'
in the generic type or method 'GenericsTest.Program.GetCollectionFromDb<T>(string)'.
There is no implicit reference conversion from'GenericsTest.MyCollection1' to
'GenericsTest.MyCollectionBase<GenericsTest.MyCollection1>'.

J'apprécierais toutes les suggestions quant à ce que je pourrais mal faire ou à la façon dont je pourrais aborder la question différemment pour atteindre le résultat escompté.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Db4objects.Db4o;

 namespace GenericsTest 
    {
        public class Entity1
        {
            public string SomeProperty { get; set; }
        }

        public class Entity2
        {
            public string SomeProperty { get; set; }
        }

        public abstract class MyCollectionBase<T> : Collection<T>
        {
            protected MyCollectionBase() : this("") { }

            protected MyCollectionBase(string pCollectionName) 
            {
                CollectionName = pCollectionName; 
            }

            public string CollectionName { get; set; }
        }

        public class MyCollection1 : MyCollectionBase<Entity1>
        {
            public MyCollection1(string pCollectionName) :
                                          base(pCollectionName) { }

            public void DoSomeWorkOnCollection1() {}
        }

        public class MyCollection2 : MyCollectionBase<Entity2>
        {
            public MyCollection2(string pCollectionName) :
                                          base(pCollectionName) { }

            public void DoSomeWorkOnCollection2() { }
        }

        public class Program
        {
            public static IObjectContainer db = null;

            public static void Main(string[] args)
            {
                const string Collection1Name = "Entity1Collection";
                const string Collection2Name = "Entity2Collection";
                db = Db4oFactory.OpenFile("Test.db");

                Entity1 entity1 = new Entity1();
                MyCollection1 collection1 = new MyCollection1(Collection1Name);
                collection1.Add(entity1);
                db.Store(collection1);

                Entity2 entity2 = new Entity2();
                MyCollection2 collection2 = new MyCollection2(Collection2Name);
                collection1.Add(entity1);
                db.Store(collection2);

                db.Commit();
                db.Close();
                db = Db4oFactory.OpenFile("Test.db");

                MyCollection1 collection3 = 
                       GetCollectionFromDb<MyCollection1>(Collection1Name);
            }

            private static T GetCollectionFromDb<T>(string pCollectionName) 
                                                 where T : MyCollectionBase<T>
            {
                IList<T> queryResult = db.Query((T c) => 
                                         c.CollectionName == pCollectionName);
                if (queryResult.Count != 0) return queryResult[0];

                return null;
            }
        } 
    }
Était-ce utile?

La solution

Suivez simplement le T:

    // ...
    {
       //...
       MyCollection1 collection3 = GetCollectionFromDb<MyCollection1>(Collection1Name);

    }

    private static T GetCollectionFromDb<T>(string pCollectionName) where T : MyCollectionBase<T>
    {
        IList<T> queryResult = db.Query((T c) => c.CollectionName == pCollectionName);
        if (queryResult.Count != 0) return queryResult[0];
        return null;
    }

se traduirait par:

    private static MyCollection1 GetCollectionFromDb<MyCollection1>(string pCollectionName) where T : MyCollectionBase< MyCollection1 >
    {
        IList< MyCollection1 > queryResult = db.Query((MyCollection1 c) => c.CollectionName == pCollectionName);
        if (queryResult.Count != 0) return queryResult[0];
        return null;
    }

Ce qui n’est pas ce que vous voulez, puisque MyCollection1 dérive de MyCollectionBase < Entity1 & Gt; et pas MyCollectionBase < MyCollection1 & Gt ;, c'est pourquoi vous avez eu l'erreur. Si vous souhaitez que la contrainte fonctionne, vous devrez probablement utiliser un deuxième identificateur de type pour exprimer le type utilisé dans la collection générique.

Autres conseils

Votre type ne satisfait pas la contrainte. Vous avez fourni MyCollection1 qui provient de MyCollection<Entity1>. Cependant, cela ne signifie pas qu'il dérive de MyCollection<MyCollection1>.

Peut-être souhaitez-vous exprimer la contrainte sur deux paramètres de type au lieu d'un:

private static T GetCollectionFromDb<T, U>(string pCollectionName) 
                                             where T : MyCollectionBase<U>

Ensuite, appelez-le avec:

GetCollectionFromDb<MyCollection1, Entity1>(Collection1Name);

Si cela ne suffit pas, dites-nous pourquoi.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top