Libreria di classi generiche - bug, descrizione, domande, caratteristiche d'uso e suggerimenti - pagina 21

 
fxsaber:

Abbiamo solo bisogno di sovraccaricare GetHashCode per il tipo richiesto invece di avviare IEqualityComparable.

In questo caso le estremità sono molto lontane e un possibile errore apparirà da qualche parte nelle profondità della libreria. E poi indovinare perché qualche funzione non viene trovata e di chi è la colpa. E le interfacce garantiscono in anticipo che tutte le funzioni necessarie sono già definite nell'oggetto.

 
Alexey Navoykov:

In questo caso, le estremità vanno molto lontano, e il possibile errore si verifica da qualche parte nelle profondità della libreria. E poi indovinare perché qualche funzione non si trova e di chi è la colpa. E le interfacce garantiscono in anticipo che tutte le funzioni necessarie sono già definite nell'oggetto.

Puoi mostrare un esempio per MqlTick?

 
Alexey Navoykov:

E le interfacce assicurano in anticipo che tutte le funzioni necessarie siano già definite nell'oggetto.

Sì, tranne che queste classi pretendono di essere universali e devono funzionare per tutti i tipi fuori dalla scatola.

 
fxsaber:

Puoi mostrare un esempio per MqlTick?

class CMqlTick : public IEqualityComparable<CMqlTick*>
{
 public: 
   MqlTick _tick;
   bool    Equals(CMqlTick* obj) { return obj!=NULL && obj._tick.time==_tick.time; }
   int     HashCode(void)        { return _tick.time; }
};
Ci sono naturalmente i costi di dover mettere una classe, più il controllo del puntatore.
 
Alexey Navoykov:
Ci sono dei costi qui, ovviamente, che dovremo mettere in un controllo di classe più puntatore.

Grazie, ma non riesco a capire quale sia il vantaggio di questo approccio nella pratica?

In SB, il codice è come questo

//+------------------------------------------------------------------+
//| Class CKeyValuePair<TKey, TValue>.                               |
//| Usage: Defines a key/value pair that can be set or retrieved.    |
//+------------------------------------------------------------------+
template<typename TKey,typename TValue>
class CKeyValuePair: public IComparable<CKeyValuePair<TKey,TValue>*>
  {
protected:
   TKey              m_key;
   TValue            m_value;

public:
                     CKeyValuePair(void)                                              {   }
                     CKeyValuePair(TKey key,TValue value): m_key(key), m_value(value) {   }
                    ~CKeyValuePair(void)                                              {   }
   //--- methods to access protected data
   TKey              Key(void)           { return(m_key);   }
   void              Key(TKey key)       { m_key=key;       }
   TValue            Value(void)         { return(m_value); }
   void              Value(TValue value) { m_value=value;   }
   //--- method to create clone of current instance
   CKeyValuePair<TKey,TValue>*Clone(void) { return new CKeyValuePair<TKey,TValue>(m_key,m_value); }
   //--- method to compare keys
   int               Compare(CKeyValuePair<TKey,TValue>*pair) { return ::Compare(m_key,pair.m_key); }
   //--- method for determining equality
   bool              Equals(CKeyValuePair<TKey,TValue>*pair) { return ::Equals(m_key,pair.m_key); }
   //--- method to calculate hash code   
   int               HashCode(void) { return ::GetHashCode(m_key); }

Si scopre che le danze proposte solo per evitare il sovraccarico di questo GetHashCode. Ma ne vale la pena in questo caso?

 
fxsaber:

Grazie, ma non riesco a capire quale sia il vantaggio di questo approccio nella pratica?

In SB, il codice è come questo

Si scopre che le danze proposte solo per evitare di sovraccaricare questo GetHashCode. Ma ne vale la pena in questo caso?

Se la velocità è fondamentale, è meglio sovraccaricarla.

Il punto è che in .NET, da cui è stata portata questa libreria, tutti i tipi built-in hanno già interfacce fin dall'inizio. Questo è il modo in cui int, alias Int32, è definito:

public struct Int32 : IComparable, IFormattable, IConvertible, 
        IComparable<int>, IEquatable<int>

Questo è il motivo per cui non c'è sovraccarico.

E la stessa classe CKeyValuePair sarebbe dichiarata un po' diversamente.

Anche se sarebbe stato meglio spendere questo tempo per migliorare la funzionalità del linguaggio, allora l'intera libreria .Net avrebbe potuto essere copiata e tutto avrebbe funzionato.

 
Alexey Navoykov:
Ci sono dei costi qui, naturalmente, che dovrete mettere in una classe, più il controllo del puntatore.
Che problema c'è a mettere questo codice nell'overload di GetHashCode? Allora non c'è bisogno di ereditare dall'interfaccia
 
Combinatore:
Che problema c'è nel ficcare questo codice nell'overload di GetHashCode? allora non c'è bisogno di ereditare dall'interfaccia

È possibile, naturalmente. Ma rende più difficile controllare il processo. Supponiamo di avere un mucchio di diversi GetHashCodes, sparsi in tutto il codice, e quali di essi sono chiamati qui (e dove si trovano), può essere difficile da capire. Per esempio quando si chiama una funzione, l'argomento è castato in qualche altro tipo. In C# questo può essere il motivo per cui le possibilità di template sono molto limitate rispetto al C++

 
Alexey Navoykov:

È possibile, naturalmente. Ma complica il controllo del processo.

In C++ questa interfaccia non è necessaria, l'interazione normale si ottiene con un semplice overloading dell'operatore <, e questo operatore può essere definito al di fuori della CLASSE.

Penso che sia molto più facile e laconico che trasformare tali costruzioni. Ma avete bisogno di un supporto nativo per sovraccaricare l'operatore < al di fuori di una classe.

Ma per le strutture incorporate non c'è niente tranne l'overloading di GetHashCode, perché lo stub è orribile e l'ereditarietà è impossibile. L'ereditarietà dalla struttura è una soluzione di stampella, perché costringe a usare funzioni standard per lanciare manualmente la struttura all'ereditarietà dell'utente, così tutto funzionerà come previsto.
 
Combinatore:

L'ereditarietà da una struttura è una soluzione di stampella, perché vi costringe a lanciare manualmente la struttura in un erede personalizzato quando usate funzioni standard per far funzionare tutto come previsto.

Apparentemente non intendi l'ereditarietà, ma il wrapping da una classe su una struttura?

In realtà, tale "forzatura" è anche uno svantaggio della funzionalità MQL, poiché non c'è modo di sovraccaricare l'operatore cast, altrimenti la classe potrebbe facilmente essere lanciata alla struttura implicitamente.

Ma nessuno si è preoccupato... Lo sviluppo è stato stagnante per 2 anni, senza miglioramenti e innovazioni.

Mentre gli sviluppatori stessi qui si lamentano della mancanza di interfacce multiple e di altre caratteristiche, ma continuano a mangiare un cactus).