Pagina 1 di 3 1 2 3 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 21
  1. #1
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308

    [C#] Metodi generici <T>

    Un saluto a tutti, ogni tanto ho bisogno di voi.
    Cercherò di essere il più chiaro e sintetico possibile, e per far questo non posterò il codice "vero" ma una sua schematizzazione.
    Io ho tre array chiamati con lo stesso nome 'faces' che rappresentano le 6 facce di un solido. Gli array sono tre perchè i tipi di elementi che contengono possono essere 3: il primo tipo (struttura) è FaceT e costruisce facce "piatte" ricoperte da una texture, il secondo è FaceH e costruisce facce "accidentate" modellate da una HeightMap ed il terzo FaceHT fa le due cose insieme.
    Per stabilire se un osservatore sta "sorvolando" una delle facce di un solido e se si quale numero di faccia serve un metodo Close() che accetta in argomento l'array faces e restituisce il numero di faccia sorvolata

    Close(faces)

    Questo metodo a sua volta ne richiama un altro che richiede pure lui l'array faces:

    Close(faces) --> IsP(faces)

    IsP verifica se la "proiezione" dell'osservatore sul piano (infinito) della faccia è interna al perimetro del rettangolo che delimita la faccia.
    Ora, devo dichiarare il tipo di array quando dichiaro Close e IsP:

    public int Close(FaceT faces) --> public bool IsP(FaceT faces)

    Ma io ho tre tipi di "facce" e dunque mi servono 6 metodi (tre coppie)

    public int CloseT(FaceT faces) --> public bool IsPT(FaceT faces)
    public int CloseH(FaceH faces) --> public bool IsPH(FaceH faces)
    public int CloseHT(FaceHT faces) --> public bool IsPHT(FaceHT faces)

    Per carità, funzionare funziona, ma per principio (e per correttezza di programmazione) ho cercato di utilizzare solo 2 metodi, in questo modo:

    public int Close<T>(T faces) --> public bool IsP<T>(T faces)

    E ci sono pure quasi riuscito, tranne per una cosa: questo errore

    Errore 1 Impossibile applicare l'indicizzazione con [] a un'espressione di tipo 'T'

    L'errore nasce dal fatto che 'faces' è un array e all'interno del secondo metodo si cerca di leggerne gli
    elementi faces[i].

    codice:
            public bool IsOn<T>(int i, T faces, Vector3 L, float closed)
            {
                bool res = false;
                Vector3 NP = My.Discovery.Position - L - faces[i].nodes[0];
                Vector3 v1 = Vector3.Normalize(faces[i].V1);
                Vector3 v2 = Vector3.Normalize(faces[i].V2);
                if ((Vector3.Dot(NP, faces[i].Normal) > My.Q2) & (Vector3.Dot(NP, faces[i].Normal) < closed))
                    if ((Vector3.Dot(NP, v1) > 0) & (Vector3.Dot(NP, v1) < faces[i].V1.Length()))
                        if ((Vector3.Dot(NP, v2) > 0) & (Vector3.Dot(NP, v2) < faces[i].V2.Length()))
                            res = true;
                return res;
            }
    Come posso risolvere?
    Ciao e grazie.
    Ultima modifica di escocat; 09-01-2014 a 17:43

  2. #2
    La funzione dovrebbe essere
    codice:
    publicboolIsOn<T>(int i, T[] faces,Vector3 L,float closed)
    Insomma come passeresti qualsiasi array :-)
    http://msdn.microsoft.com/en-us/library/hyfeyz71.aspx

  3. #3
    Ah no aspetta forse avevo capito male... Quando dici che Faces È un array, intendi che internamente è come un array ed implementa un indexer?

    Riguardando i "vecchi" prototipi di funzione sembra che sia così... In questo caso te la puoi sbolognare con l'ereditarietà oppure specificando i tipi del generic
    codice:
    (where T is ...)

  4. #4
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Dunque, la forma più giusta sarebbe

    codice:
    public bool IsOn<T>(int i, T[] faces, Vector3 L, float closed) 
    { 
        bool res = false; 
        Vector3 NP = My.Discovery.Position - L - faces[i].nodes[0]; 
        Vector3 v1 = Vector3.Normalize(faces[i].V1); 
        Vector3 v2 = Vector3.Normalize(faces[i].V2); 
        if ((Vector3.Dot(NP, faces[i].Normal) > My.Q2) & (Vector3.Dot(NP, faces[i].Normal) < closed)) 
           if ((Vector3.Dot(NP, v1) > 0) & (Vector3.Dot(NP, v1) < faces[i].V1.Length())) 
               if ((Vector3.Dot(NP, v2) > 0) & (Vector3.Dot(NP, v2) < faces[i].V2.Length())) 
                  res = true; 
        return res; 
    }
    perchè faces è un array di 6 elementi in cui ogni elemento è - a seconda del tipo - una faccia FaceT, FaceH o FaceHT. Quindi faces può contenere 6 elementi FaceT oppure 6 elementi FaceH oppure 6 elementi FaceHT.
    Ad esempio

    public struct FaceT
    {
    public Vector3[] nodes; //nodi
    public Vector3 V1;//vettori laterali (dimensionali)
    public Vector3 V2;
    public Vector3 Normal;//normale al piano zero
    public Vector3 Center;//centro della faccia
    public Texture2D Texture;
    public VertexPositionNormalTexture[] vertices;
    .......

    Ma in questa forma mi dà l'errore

    Error 4 'T' does not contain a definition for 'Normal' and no extension method 'Normal' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)

    Avevo anche provato where T : struct e ancora mi diceva che Normal non è ecc. ecc.
    Perfino where T : FaceT, FaceH, FaceHT ma la risposta è stata

    Error 4 'Progetto_Albireo.FaceT' is not a valid constraint. A type used as a constraint
    must be an interface, a non-sealed class or a type parameter.

  5. #5
    Ok allora implementa una interfaccia comune a tutti, con un metodo virtuale puro Normal
    E Dai Dai Dai !!!

  6. #6
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    What?

  7. #7
    Utente di HTML.it L'avatar di U235
    Registrato dal
    Mar 2006
    Messaggi
    1,536
    Ciao,
    per il parametro faces devi usare il tipo IList<T> :

    codice:
    publicboolIsOn<T>(int i,IList<T> faces,Vector3 L,float closed) 
    { 
            bool res =false; 
            Vector3 NP =My.Discovery.Position- L - faces[i].nodes[0]; 
            Vector3 v1 =Vector3.Normalize(faces[i].V1); 
            Vector3 v2 =Vector3.Normalize(faces[i].V2); 
            if((Vector3.Dot(NP, faces[i].Normal)>My.Q2)&(Vector3.Dot(NP, faces[i].Normal)< closed)) 
            if((Vector3.Dot(NP, v1)>0)&(Vector3.Dot(NP, v1)< faces[i].V1.Length())) 
            if((Vector3.Dot(NP, v2)>0)&(Vector3.Dot(NP, v2)< faces[i].V2.Length())) 
            res =true; 
            return res; 
    }
    EDIT :
    per capire il perché, è sufficiente pensare che T è un tipo, T[] (in caso) sarebbe un tipo diverso che deriva da IList<T>, ovvero un array di T, non un tipo T e basta.
    Ultima modifica di U235; 11-01-2014 a 18:35

  8. #8
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Niente, mi dà sempre lo stesso errore di prima. Posto il codice chiamante:

    codice:
                    //1. Troviamo il n. della faccia sorvolata
                    //per questo ci servono gli array faces del modello 
                    //ma non sappiamo se sono di tipo FaceH, FaceT o FaceHT
                    finfo = My.Planets[k].GetType().GetField("faces");
                    if (ThisType == My.NormalTType)
                    {
                        FaceT[] faces = (FaceT[])finfo.GetValue(My.Planets[k]);
                        //Vediamo se c'è una faccia sorvolata
                        int c = Close<FaceT>(faces, L, closed);
                        if (c != 999)//la Discovery sta sorvolando una faccia 
                        {
                            
                            //proiezione normale sulla faccia nel rif. assoluto (O)
                            Vector3 P = Pnormal<FaceT>(faces[c], L);
                            //rif. generalizzato di P al nodo principale (N)
                            Vector3 NP = P - L - faces[c].nodes[0];
                            //Controlla se la proiezione P è interna ai bordi della faccia sorvolata
                            if (IsP<FaceT>(NP, faces[c]))
                            {
                                //SI. -->  P E' L'OMBRA DELLA DISCOVERY
                                //quota sul piano zero. 
                                ThisQuote = Vector3.Dot(My.Discovery.Position - P, faces[c].Normal);
                                if (ThisQuote < 1f)// --> ATTERRATA!
                                {
                                    //Calcola l'orientamento del piano zero della faccia di atterraggio
                                    Vector3 V1 = faces[c].V1;
                                    Vector3 n = faces[c].Normal;
                                    //Imposta le relactions attuali
                                    My.GMode = false;
                                    My.TMode = false;
                                    My.OMode = false;
                                    //My.GMode = false;
                                    Position = P + 1f * n;//vertice al suolo + 1
                                    Speed = 0f;
                                    To = Vector3.Normalize(V1);
                                    Azi = azif(To);
                                    Alt = altf(To);
                                    Up = n;
                                    
                                    //Aggiorna ThisQuote con la nuova posizione
                                    ThisQuote = 1f;
                                    Ground = true;
                                    TPush = 4f * D * ES * My.Gfactor;
                                    
    
                                }
                            }
                        }
                    }
                    else if (ThisType == My.NormalHType)
                    {
                        FaceH[] faces = (FaceH[])finfo.GetValue(My.Planets[k]);
                        //Vediamo se c'è una faccia sorvolata
                        int c = Close<FaceH>(faces, L, closed);
                        if (c != 999)//la Discovery sta sorvolando una faccia 
                        {
                            
                            float Qzero = 999f; //inizializza altezza della Discovery dal piano di faccia
                            float DeltaQ = 0f; // e il dislivello superficiale rispetto a Qzero.
                            
                            //proiezione normale sulla faccia nel rif. assoluto (O)
                            Vector3 P = Pnormal<FaceH>(faces[c], L);
                            //rif. generalizzato di P al nodo principale (N)
                            Vector3 NP = P - L - faces[c].nodes[0];
                            //Controlla se la proiezione P è interna ai bordi della faccia sorvolata
                            if (IsP<FaceH>(NP, faces[c]))
                            {
                                //SI. -->  P E' L'OMBRA DELLA DISCOVERY
                                
                                //quota sul piano zero. 
                                Qzero = Vector3.Dot(My.Discovery.Position - P, faces[c].Normal);
                                
                                //dislivello della superficie in altezza o profondità rispetto al piano zero
                                DeltaQ = Delta<FaceH>(P, faces[c], L);
                                ThisQuote = Qzero - DeltaQ;
                                if (ThisQuote < 1f)// --> ATTERRATA!
                                {
                                    //Calcola l'orientamento del piano zero della faccia di atterraggio
                                    Vector3 V1 = faces[c].V1;
                                    Vector3 n = faces[c].Normal;
                                    //Imposta le relactions attuali
                                    My.GMode = false;
                                    My.TMode = false;
                                    My.OMode = false;
                                    Position = P + (DeltaQ + 1) * n;//vertice al suolo + 1
                                    Speed = 0f;
                                    To = Vector3.Normalize(V1);
                                    Azi = azif(To);
                                    Alt = altf(To);
                                    Up = n;
                                    //Aggiorna Qzero e Quote con la nuova posizione
                                    Qzero = DeltaQ + 1f;
                                    ThisQuote = 1f;
                                    Ground = true;
                                    TPush = 4f * D * ES * My.Gfactor;
                                    TPush *= 30f + 27 * (1f - D * ES) / 999999f;
                                }
                            }
                        }
     
                    }
                    else//NormalHTType
                    {
                        FaceHT[] faces = (FaceHT[])finfo.GetValue(My.Planets[k]);
                        //Vediamo se c'è una faccia sorvolata
                        int c = Close<FaceHT>(faces, L, closed);
                        if (c != 999)//la Discovery sta sorvolando una faccia 
                        {
                            float Qzero = 999f; //inizializza altezza della Discovery dal piano di faccia
                            float DeltaQ = 0f; // e il dislivello superficiale rispetto a Qzero.
                            //proiezione normale sulla faccia nel rif. assoluto (O)
                            Vector3 P = Pnormal<FaceHT>(faces[c], L);
                            //rif. generalizzato di P al nodo principale (N)
                            Vector3 NP = P - L - faces[c].nodes[0];
                            //Controlla se la proiezione P è interna ai bordi della faccia sorvolata
                            if (IsP<FaceHT>(NP, faces[c]))
                            {
                                //SI. -->  P E' L'OMBRA DELLA DISCOVERY
                                //quota sul piano zero. 
                                Qzero = Vector3.Dot(My.Discovery.Position - P, faces[c].Normal);
                                //dislivello della superficie in altezza o profondità rispetto al piano zero
                                DeltaQ = Delta<FaceHT>(P, faces[c], L);
                                ThisQuote = Qzero - DeltaQ;
                                if (ThisQuote < 1f)// --> ATTERRATA!
                                {
                                    //Calcola l'orientamento del piano zero della faccia di atterraggio
                                    Vector3 V1 = faces[c].V1;
                                    Vector3 n = faces[c].Normal;
                                    //Imposta le relactions attuali
                                    My.GMode = false;
                                    My.TMode = false;
                                    My.OMode = false;
                                    Position = P + (DeltaQ + 1) * n;//vertice al suolo + 1
                                    Speed = 0f;
                                    To = Vector3.Normalize(V1);
                                    Azi = azif(To);
                                    Alt = altf(To);
                                    Up = n;
                                    //Aggiorna Qzero e Quote con la nuova posizione
                                    Qzero = DeltaQ + 1f;
                                    ThisQuote = 1f;
                                    Ground = true;
                                    TPush = 4f * D * ES * My.Gfactor;
                                    TPush *= 30f + 27 * (1f - D * ES) / 999999f;
                                }
                            }
                        }
                    }

    e queste sono le funzioni incriminate:


    codice:
     
    
    
            //Calcola, se esiste, il numero della faccia sorvolata dalla Discovery 
            public int Close<T>(IList<T> faces, Vector3 L, float closed)
            {
                int res = 999;
                for (int i = 0; i < 6; i++)
                {
                    T face = faces[i];
                    if (IsOn<T>(face, L, closed))//condizione di sorvolo
                    {
                        res = i;
                        break;
                    }
                }
                return res;
            }
    
            //verifica che la Discovery si trova nello spazio sovrastante 
            //(sta sorvolando) una faccia 
            public bool IsOn<T>(T face, Vector3 L, float closed)
            {
                bool res = false;
                //Position nel riferimento al nodo principale N della faccia i-esima
                Vector3 NP = My.Discovery.Position - L - face.nodes[0];
                //versori dei vettori direttivi della faccia i-esima
                Vector3 v1 = Vector3.Normalize(face.V1);
                Vector3 v2 = Vector3.Normalize(face.V2);
                if ((Vector3.Dot(NP, face.Normal) > My.Q2) & (Vector3.Dot(NP, face.Normal) < closed))
                    if ((Vector3.Dot(NP, v1) > 0) & (Vector3.Dot(NP, v1) < face.V1.Length()))
                        if ((Vector3.Dot(NP, v2) > 0) & (Vector3.Dot(NP, v2) < face.V2.Length()))
                            res = true;
                return res;
            }
     
            //verifica che la proiezione di un punto P su un piano è interno   
            //alla faccia (ombra)
            public bool IsP<T>(Vector3 P, T face)
            {
                bool res = false;
                //versori dei vettori direttivi della faccia i-esima
                Vector3 v1 = Vector3.Normalize(face.V1);
                Vector3 v2 = Vector3.Normalize(face.V2);
                //Verifica se P è interno al parallelogramma costruito con i due vettori direttivi V1 e V2
                //calcolando le proiezioni del vettore P sui versori v1 e v2
                if ((Vector3.Dot(P, v1) > 0) & (Vector3.Dot(P, v1) < face.V1.Length()))
                    if ((Vector3.Dot(P, v2) > 0) & (Vector3.Dot(P, v2) < face.V2.Length()))
                        res = true;
                return res;
            }
            //calcola la proiezione normale P della Discovery sulla faccia i-esima
            public Vector3 Pnormal<T>(T face, Vector3 L)
            {
                //Nodo principale e normale
                Vector3 N = L + face.nodes[0];
                Vector3 n = face.Normal;
                return My.Discovery.Position + Vector3.Dot(N - My.Discovery.Position, n) * n;
            }
            //calcola il dislivello della forma superficiale di una faccia
            //nel punto P sul piano zero
            public float Delta<T>(Vector3 P, T face, Vector3 L)
            {
                Vector3 N = L + face.nodes[0];
                Vector3 v1 = Vector3.Normalize(face.V1);
                Vector3 v2 = Vector3.Normalize(face.V2);
                int u = (int)Vector3.Dot(P - N, v1);
                int w = (int)Vector3.Dot(P - N, v2);
                //indice del vertice corrispondente
                int index = (int)(u + w * (face.V1.Length() + 1));
                //Delta (negativo se depresso)
                return face.generalizeV[index].Position.Y;
            }

  9. #9
    Utente di HTML.it L'avatar di U235
    Registrato dal
    Mar 2006
    Messaggi
    1,536
    perdonami ma non posso controllare tutto il codice, non ne avrei il tempo...
    Il problema è che devi saperlo tu come di che tipo è Faces, se si tratta di un array (tipo miaclasse[]), allora è un tipo IList<miaclasse>, se invece quello che passi è un miaclasse, allora T e basta, in fine se quello che passi è un T, ma con indicizzatori (non è un array...) allora non lo puoi usare con generics direttamente, ma devi creare un interface :

    codice:
    interface IIndexable<T>
    {
         T this[string index] {get; set;}
    }
    dalla quale fai ereditare tua classe (il tipo di Faces).
    a questo punto :
    codice:
    public bool Close<T,A>(T faces, Vector3 L, float closed) where T : IIndexable<A>
    {
         .....
         A NP = faces[0];
    }
    ....
    //per chiamarlo :
    Close<Face<string>, string>(new Face<string>(), myVector3, myFloat);
    questo nel caso il valore restituito dall'indicizzatore sia di tipo string (A), diversamente per A usi il tipo che restituisce.

    ora vista così non è proprio il massimo, bisognerebbe capire che tipo restituisce l'indexer, se è dello stesso tipo della classe (T) allora hai sbagliato a fare gli indicizzatori, sarebbe stato meglio usare un array di tipo T (T senza indici).

  10. #10
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ok faccio un esempio per farmi capire. In questo esempio uso le classi anzichè le strutture
    perchè così mi è più comodo scrivere costruttori senza parametri.
    Dunque ho due classi di oggetti che rappresentano figure solide: NormalT e NormalH

    codice:
    public class NormalT
    {
    public FaceT[] faces;
    
    }
    
    
    public class NormalH
    {
    public FaceH[] faces;
    
    }
    
    Sia l'uno che l'altro tipo espone lo stesso campo 'faces' perchè ogni oggetto, di qualunque tipo
    sia, ha sei facce: faces è sempre un array di 6 elementi, ogni elemento è una faccia del solido.
    In particolare le facce del tipo NormalT sono di tipo FaceT mentre quelle del tipo NormalH sono
    di tipo FaceH, ma comunque sia ogni faccia ha 4 "spigoli" (nodi) che per semplicità consideriamo
    dei semplici numeri interi:


    codice:
    public class FaceT
    {
    public int[] nodes;
    }
    
    public class FaceH
    {
    public int[] nodes;
    
    }
    
    Per poter costruire un tipo (NormalT o NormalH) serve prima poter costruire una faccia (FaceT o
    FaceH); per inventarci una distinzione tra FaceT e FaceH (quindi tra NormalT e NormalH)
    stabiliamo che FaceT abbia per nodi numeri pari mentre FaceH abbia 4 nodi dispari:

    codice:
    public class FaceT
    {
    public int[] nodes;
    public FaceT()
    {
    nodes = new int[4];
    nodes[0] = 0;
    nodes[1] = 2;
    nodes[2] = 4;
    nodes[3] = 6;
    }
    }
    
    public class FaceH
    {
    public int[] nodes;
    public FaceH()
    {
    nodes = new int[4];
    nodes[0] = 1;
    nodes[1] = 3;
    nodes[2] = 5;
    nodes[3] = 7;
    
    }
    }
    
    Non ci resta che scrivere i costruttori di NormalT e NormalH:

    codice:
    public class NormalT
    {
    public FaceT[] faces;
    public NormalT()
    {
    faces = new FaceT[6];
    faces[0] = new FaceT();
    faces[1] = new FaceT();
    faces[2] = new FaceT();
    faces[3] = new FaceT();
    faces[4] = new FaceT();
    faces[5] = new FaceT();
    }
    
    
    }
    public class NormalH
    {
    public FaceH[] faces;
    public NormalH()
    {
    faces = new FaceH[6];
    faces[0] = new FaceH();
    faces[1] = new FaceH();
    faces[2] = new FaceH();
    faces[3] = new FaceH();
    faces[4] = new FaceH();
    faces[5] = new FaceH();
    }
    
    }
    
    Bene, adesso vogliamo calcolare la "somma" dei nodi di una faccia, qualsiasi faccia di qualsiasi
    tipo sia. E' chiaro che in argomento al metodo Sum dobbiamo passare un parametro 'face' cioè una
    faccia

    codice:
    public static Sum(..... face)
    {
    int sum=0;
    for(int i=0; i<4; i++)
    sum+=face.nodes[i];
    return sum;
    }
    
    Purtroppo occorre specificare un tipo per il parametro 'face', che può essere di tipo FaceT ma
    anche FaceH. Dunque viene spontaneo scrivere 2 metodi

    codice:
    public static int SumT(FaceT face)
    {
    int sum=0;
    for(int i=0; i<4; i++)
    sum+=face.nodes[i];
    return sum;
    }
    
    public static int SumH(FaceH face)
    {
    int sum=0;
    for(int i=0; i<4; i++)
    sum+=face.nodes[i];
    return sum;
    }
    


    Se invece vogliamo scrivere un unico metodo Sum che valga per ogni tipo di faccia tentiamo:

    codice:
    public static int Sum<T>(T face)
    {
    int sum=0;
    for(int i=0; i<4; i++)
    sum+=face.nodes[i];
    return sum;
    }
    
    ed ecco che il compilatore si ribella e mi sottolinea in rosso 'nodes' dicendomi:

    Errore 1 'T' non contiene una definizione di 'nodes' e non è stato trovato alcun metodo di estensione 'nodes' che accetta un primo argomento di tipo 'T'. Probabilmente manca una direttiva using o un riferimento a un assembly.

    BOCCIATO!
    Ultima modifica di escocat; 11-01-2014 a 22:57

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.