Domanda

Sto facendo qualcosa di sbagliato o non è possibile specificare una classe generica come vincolo a un metodo generico?

Ho giocato con generics e db4o (database di oggetti open source) e sto scrivendo un test programma (vedi codice sotto) per archiviare e recuperare alcune raccolte generiche definite dall'utente.

Sto tentando di scrivere un metodo generico (vedi GetCollectionFromDb di seguito) per recuperare una raccolta specificata dal database. Sfortunatamente, il codice seguente restituisce un errore generato dal compilatore per la riga:

 MyCollection1 collection3 =
                  GetCollectionFromDb<MyCollection1>(Collection1Name);

Il messaggio di errore è:

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>'.

Gradirei qualsiasi suggerimento su cosa potrei fare di sbagliato o su come potrei affrontarlo in modo diverso per raggiungere il risultato desiderato.

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;
            }
        } 
    }
È stato utile?

Soluzione

Segui semplicemente la 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;
    }

si tradurrebbe in:

    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;
    }

Che non è quello che vuoi poiché MyCollection1 deriva da MyCollectionBase < Entity1 & Gt; e non MyCollectionBase < MyCollection1 & Gt ;, ecco perché hai ricevuto l'errore. Se vuoi che il vincolo funzioni, probabilmente dovrai utilizzare un secondo identificatore di tipo per esprimere il tipo utilizzato nella raccolta generica.

Altri suggerimenti

Il tuo tipo non soddisfa il vincolo. Hai fornito MyCollection1 che deriva da MyCollection<Entity1>. Tuttavia, ciò non significa che derivi da MyCollection<MyCollection1>.

Forse vuoi esprimere il vincolo su due parametri di tipo anziché su uno:

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

Quindi chiamalo con:

GetCollectionFromDb<MyCollection1, Entity1>(Collection1Name);

Se ciò non risolve il problema, facci sapere perché.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top