Condividi tramite


Common Type System

Common Type System definisce le modalità di dichiarazione, utilizzo e gestione dei tipi in Common Language Runtime e rappresenta una parte importante del supporto runtime per l'integrazione di più linguaggi. Le funzioni assolte dal sistema di tipi comuni sono le seguenti:

  • Definire un framework che consenta l'integrazione di più linguaggi, l'indipendenza dai tipi e l'esecuzione di codice con prestazioni elevate.

  • Fornire un modello orientato a oggetti che supporta l'implementazione completa di molti linguaggi di programmazione.

  • Definire le regole che i linguaggi devono seguire, garantendo l'interazione tra oggetti scritti in linguaggi diversi.

  • Fornire una libreria che contiene i tipi di dati primitivi, ad esempio Boolean, Byte, Char, Int32 e UInt64, utilizzati nello sviluppo delle applicazioni.

Di seguito sono elencate le diverse sezioni di questo argomento:

  • Tipi in .NET Framework

  • Definizioni dei tipi

  • Membri dei tipi

  • Caratteristiche dei membri dei tipi

Tipi in .NET Framework

Tutti i tipi in .NET Framework sono tipi di valore o tipi di riferimento.

I tipi di valore sono tipi di dati i cui oggetti sono rappresentati dal valore effettivo dell'oggetto. Se un'istanza di un tipo di valore viene assegnata a una variabile, a tale variabile viene fornita una copia aggiornata del valore.

I tipi di riferimento sono tipi di dati i cui oggetti sono rappresentati da un riferimento (simile a un puntatore) al valore effettivo dell'oggetto. Se un tipo di riferimento viene assegnato a una variabile, tale variabile fa riferimento (punta) al valore originale. Non viene effettuata alcuna copia.

Common Type System in .NET Framework supporta le cinque categorie di tipi seguenti:

  • Classi

  • Strutture

  • Enumerazioni

  • Interfacce

  • Delegati

Classi

Una classe è un tipo di riferimento che è possibile derivare direttamente da un'altra classe e che viene derivato in modo implicito da System.Object. La classe definisce le operazioni che un oggetto (un'istanza della classe) può eseguire (metodi, eventi o proprietà) e i dati che l'oggetto contiene (campi). Sebbene una classe includa in genere sia la definizione che l'implementazione, a differenza delle interfacce che contengono, ad esempio, solo la definizione senza l'implementazione, può contenere uno o più membri privi di implementazione.

Nella tabella seguente vengono descritte alcune delle caratteristiche che una classe può avere. Ogni linguaggio che supporta il runtime fornisce un modo per indicare che una classe o membro di classe dispone di una o più di queste caratteristiche. È tuttavia possibile che i singoli linguaggi di programmazione destinati a .NET Framework non rendano disponibili tutte queste caratteristiche.

Caratteristica

Descrizione

sealed

Specifica che da questo tipo non è possibile derivare un'altra classe.

implements

Indica che la classe utilizza una o più interfacce fornendo implementazioni dei membri di interfaccia.

abstract

Indica che non è possibile creare un'istanza della classe. Per utilizzarla è necessario derivare da essa un'altra classe.

inherits

Indica che le istanze della classe possono essere utilizzate ovunque la classe sia specificata. Una classe derivata che eredita da una classe di base può utilizzare l'implementazione di qualsiasi membro pubblico fornito dalla classe di base oppure la classe derivata può eseguire l'override dell'implementazione dei membri pubblici con la propria implementazione.

exported o not exported

Indica se una classe è visibile all'esterno dell'assembly in cui è definita. Questa caratteristica è applicabile unicamente alle classi di primo livello e non alle classi annidate.

NotaNota

Una classe può anche essere annidata in una struttura o una classe padre.Anche le classi annidate possiedono le caratteristiche dei membri.Per ulteriori informazioni, vedere Tipi annidati.

I membri di classe privi di implementazione sono membri astratti. Una classe con uno o più membri astratti è essa stessa astratta e non è possibile crearne nuove istanze. Con alcuni linguaggi destinati al runtime è possibile contrassegnare una classe come astratta anche se nessuno dei relativi membri è astratto. È possibile utilizzare una classe astratta quando si desidera incapsulare un set di base di funzionalità che le classi derivate possono ereditare oppure sottoporre a override nelle circostanze appropriate. Alle classi che non sono astratte viene fatto riferimento come a classi concrete.

Una classe può implementare un numero qualsiasi di interfacce, ma può ereditare solo da una classe di base, oltre che da System.Object, da cui tutte le classi ereditano in modo implicito. Tutte le classi devono avere almeno un costruttore, per l'inizializzazione di nuove istanze della classe. Se non si definisce in modo esplicito un costruttore, la maggior parte dei compilatori fornisce automaticamente un costruttore predefinito (senza parametri).

Strutture

Una struttura è un tipo di valore che deriva in modo implicito da System.ValueType, che a sua volta deriva da System.Object. Una struttura è molto utile per la rappresentazione di valori con requisiti di memoria piccoli e per passare valori come parametri per valori a metodi che dispongono di parametri fortemente tipizzati. Nella libreria di classi .NET Framework tutti i tipi di dati primitivi (Boolean, Byte, Char, DateTime, Decimal, Double, Int16, Int32, Int64, SByte, Single, UInt16, UInt32 e UInt64) sono definiti come strutture.

Analogamente alle classi, le strutture definiscono sia i dati (i campi della struttura) che le operazioni che è possibile eseguire s tali dati (i metodi della struttura). Ciò significa che è possibile chiamare metodi nelle strutture, inclusi i metodi virtuali definiti nelle classi System.Object e System.ValueType e qualsiasi metodo definito nel tipo di valore stesso. In altre parole, le strutture possono disporre di campi, proprietà ed eventi, nonché di metodi statici e non statici. È possibile creare istanze di strutture, passarle come parametri, archiviarle come variabili locali oppure in un campo di un altro tipo di valore o tipo di riferimento. Le strutture possono inoltre implementare interfacce.

I tipi di valore differiscono dalle classi per diversi motivi. Innanzitutto, anche se ereditano in modo implicito da System.ValueType, non possono ereditare direttamente da nessun tipo. Analogamente, tutti i tipi di valore sono sealed, ovvero nessun altro tipo può essere derivato da essi. Non richiedono inoltre costruttori.

Per ogni tipo di valore, Common Language Runtime fornisce un tipo sottoposto a boxing corrispondente, ovvero una classe avente lo stesso stato e lo stesso comportamento del tipo di valore. Per un'istanza di un tipo di valore viene eseguita la conversione boxing quando viene passata a un metodo che accetta un parametro di tipo System.Object. La conversione unboxing, ovvero la conversione da un'istanza di una classe di nuovo in un'istanza di un tipo di valore, viene eseguita quando il controllo viene restituito da una chiamata al metodo che accetta un tipo di valore come parametro per riferimento. In alcuni linguaggi è richiesto l'utilizzo di una sintassi speciale quando il tipo sottoposto a boxing è obbligatorio, mentre in altri il tipo sottoposto a boxing viene utilizzato automaticamente quando è necessario. Quando si definisce un tipo di valore si sta definendo sia il tipo boxed che il tipo unboxed.

Enumerazioni

Un'enumerazione (enum) è un tipo di valore che eredita direttamente da System.Enum e che fornisce nomi alternativi per i valori di un tipo primitivo sottostante. Un tipo di enumerazione dispone di un nome, un tipo sottostante che deve essere uno dei tipi Signed Integer o Unsigned Integer predefiniti, ad esempio Byte, Int32 o UInt64, e di un set di campi. I campi sono campi letterali statici, ognuno dei quali rappresenta una costante. Lo stesso valore può essere assegnato a più campi. In questo caso, è necessario contrassegnare uno dei valori come valore di enumerazione primario a scopo di reflection e conversione di stringhe.

È possibile assegnare a un'enumerazione un valore del tipo sottostante e viceversa. Nel runtime non è richiesto alcun cast. È possibile creare un'istanza di un'enumerazione e chiamare i metodi di System.Enum, nonché qualsiasi metodo definito nel tipo sottostante dell'enumerazione. In alcuni linguaggi, tuttavia, potrebbe non essere possibile passare un'enumerazione come parametro quando un'istanza del tipo sottostante è obbligatoria (o viceversa).

Alle enumerazioni si applicano le seguenti ulteriori restrizioni:

  • La definizione dei metodi non può essere eseguita direttamente dall'enumerazione.

  • Con un'enumerazione non è possibile implementare un'interfaccia.

  • Con un'enumerazione non è possibile definire proprietà o eventi.

  • Le enumerazioni non possono essere generiche, a meno che non siano generiche solo perché annidate all'interno di un tipo generico. In altre parole, un'enumerazione non può disporre di parametri dei tipi propri.

    NotaNota

    I tipi annidati, incluse le enumerazioni, creati con Visual Basic, C# e C++ comprendono i parametri di tutti i tipi generici che li comprendono e sono pertanto generici anche se non dispongono di propri parametri di tipi.Per ulteriori informazioni, vedere "Tipi annidati" nell'argomento di riferimento Type.MakeGenericType.

L'attributo FlagsAttribute denota un tipo speciale di enumerazione definito campo di bit. Nel runtime non viene fatta distinzione tra enumerazioni tradizionali e campi di bit, ma è possibile che tale distinzione esista nel linguaggio utilizzato. Quando tale distinzione viene effettuata, gli operatori bit per bit possono essere utilizzati sui campi di bit, ma non sulle enumerazioni, per generare valori non denominati. Le enumerazioni sono in genere utilizzate per elenchi di elementi univoci, come giorni della settimana o nomi di paesi o province. I campi di bit sono normalmente utilizzati per elenchi di qualità o quantità che possono ricorrere in combinazioni, come Red And Big And Fast.

Nell'esempio che segue viene illustrato come utilizzare sia i campi di bit, sia le enumerazioni tradizionali.

Imports System.Collections.Generic

' A traditional enumeration of some root vegetables.
Public Enum SomeRootVegetables
   HorseRadish
   Radish
   Turnip
End Enum 

' A bit field or flag enumeration of harvesting seasons.
<Flags()> Public Enum Seasons
   None = 0
   Summer = 1
   Autumn = 2
   Winter = 4
   Spring = 8
   All = Summer Or Autumn Or Winter Or Spring
End Enum 

' Entry point.
Public Class Example
   Public Shared Sub Main()
      ' Hash table of when vegetables are available.
      Dim AvailableIn As New Dictionary(Of SomeRootVegetables, Seasons)()

      AvailableIn(SomeRootVegetables.HorseRadish) = Seasons.All
      AvailableIn(SomeRootVegetables.Radish) = Seasons.Spring
      AvailableIn(SomeRootVegetables.Turnip) = Seasons.Spring Or _
                                               Seasons.Autumn

      ' Array of the seasons, using the enumeration.
      Dim theSeasons() As Seasons = {Seasons.Summer, Seasons.Autumn, _
                                     Seasons.Winter, Seasons.Spring}

      ' Print information of what vegetables are available each season.
      For Each season As Seasons In theSeasons
         Console.WriteLine(String.Format( _
              "The following root vegetables are harvested in {0}:", _
              season.ToString("G")))
         For Each item As KeyValuePair(Of SomeRootVegetables, Seasons) In AvailableIn
            ' A bitwise comparison.
            If(CType(item.Value, Seasons) And season) > 0 Then
               Console.WriteLine("  " + _
                     CType(item.Key, SomeRootVegetables).ToString("G"))
            End If
         Next
      Next
   End Sub 
End Class 
' The example displays the following output:
'    The following root vegetables are harvested in Summer:
'      HorseRadish
'    The following root vegetables are harvested in Autumn:
'      Turnip
'      HorseRadish
'    The following root vegetables are harvested in Winter:
'      HorseRadish
'    The following root vegetables are harvested in Spring:
'      Turnip
'      Radish
'      HorseRadish
using System;
using System.Collections.Generic;

// A traditional enumeration of some root vegetables.
public enum SomeRootVegetables
{
    HorseRadish,
    Radish,
    Turnip
}

// A bit field or flag enumeration of harvesting seasons.
[Flags]
public enum Seasons
{
    None = 0,
    Summer = 1,
    Autumn = 2,
    Winter = 4,
    Spring = 8,
    All = Summer | Autumn | Winter | Spring
}

public class Example
{
   public static void Main()
   {
       // Hash table of when vegetables are available.
       Dictionary<SomeRootVegetables, Seasons> AvailableIn = new Dictionary<SomeRootVegetables, Seasons>();

       AvailableIn[SomeRootVegetables.HorseRadish] = Seasons.All;
       AvailableIn[SomeRootVegetables.Radish] = Seasons.Spring;
       AvailableIn[SomeRootVegetables.Turnip] = Seasons.Spring | 
            Seasons.Autumn;

       // Array of the seasons, using the enumeration.
       Seasons[] theSeasons = new Seasons[] { Seasons.Summer, Seasons.Autumn, 
            Seasons.Winter, Seasons.Spring };

       // Print information of what vegetables are available each season.
       foreach (Seasons season in theSeasons)
       {
          Console.Write(String.Format(
              "The following root vegetables are harvested in {0}:\n", 
              season.ToString("G")));
          foreach (KeyValuePair<SomeRootVegetables, Seasons> item in AvailableIn)
          {
             // A bitwise comparison.
             if (((Seasons)item.Value & season) > 0)
                 Console.Write(String.Format("  {0:G}\n", 
                      (SomeRootVegetables)item.Key));
          }
       }
   }
}
// The example displays the following output:
//    The following root vegetables are harvested in Summer:
//      HorseRadish
//    The following root vegetables are harvested in Autumn:
//      Turnip
//      HorseRadish
//    The following root vegetables are harvested in Winter:
//      HorseRadish
//    The following root vegetables are harvested in Spring:
//      Turnip
//      Radish
//      HorseRadish

Interfacce

Un'interfaccia definisce un contratto che specifica una relazione di tipo "può" o una relazione di tipo "ha". Le interfacce vengono in genere utilizzate per implementare funzionalità, ad esempio il confronto e l'ordinamento (interfacce IComparable e IComparable<T>), il test di uguaglianza (interfaccia IEquatable<T>) o l'enumerazione di elementi in un insieme (interfacce IEnumerable e IEnumerable<T>). Le interfacce possono disporre di proprietà, metodi ed eventi, ovvero tutti membri astratti. Per questo motivo, anche se l'interfaccia definisce i membri e le relative firme, il compito di definire le funzionalità di ogni membro dell'interfaccia viene lasciato al tipo che la implementa. Ciò significa che qualsiasi classe o struttura che implementa un'interfaccia deve fornire le definizioni per i membri astratti dichiarati nell'interfaccia. Un'interfaccia può richiedere che qualsiasi classe o struttura che la implementa implementi anche una o più altre interfacce.

Alle interfacce si applicano le seguenti restrizioni:

  • Un'interfaccia può essere dichiarata con qualsiasi accessibilità ma i membri di interfaccia devono tutti avere accessibilità pubblica.

  • Con un'interfaccia non è possibile definire costruttori.

  • Le interfacce non possono definire campi.

  • Le interfacce possono definire solo membri di istanza. Non possono definire membri statici.

Ogni linguaggio deve fornire regole per il mapping di un'implementazione all'interfaccia che richiede il membro, in quanto più interfacce possono dichiarare un membro con la stessa firma e i membri possono disporre di implementazioni separate.

Delegati

I delegati sono tipi di riferimento che assolvono a una funzione simile a quella dei puntatori a funzione in C++. Vengono utilizzati per i gestori di eventi e le funzioni di callback in .NET Framework. Diversamente dai puntatori a funzione, i delegati sono sicuri, verificabili e indipendenti dai tipi. Un tipo delegato può rappresentare qualsiasi metodo di istanza o metodo statico con una firma compatibile.

Un parametro di un delegato è compatibile con il parametro di un metodo corrispondente se il tipo del parametro del delegato è più restrittivo rispetto al tipo del parametro del metodo. In questo modo si garantisce che un argomento passato al delegato possa essere passato in modo sicuro al metodo.

Analogamente, il tipo restituito di un delegato è compatibile con il tipo restituito di un metodo se il tipo restituito del metodo è più restrittivo rispetto al tipo restituito del delegato. In questo modo si garantisce la possibilità di eseguire in modo sicuro il cast del valore restituito del metodo nel tipo restituito del delegato.

Un delegato con un parametro del tipo IEnumerable e un tipo restituito Object può, ad esempio, rappresentare un metodo con un parametro del tipo Object e un valore restituito del tipo IEnumerable. Per ulteriori informazioni e un codice di esempio, vedere Delegate.CreateDelegate(Type, Object, MethodInfo).

Un delegato è associato al metodo che rappresenta. Oltre a essere associato al metodo, un delegato può essere associato a un oggetto. L'oggetto rappresenta il primo parametro del metodo e viene passato al metodo ogni volta che viene richiamato il delegato. Se il metodo è un metodo di istanza, l'oggetto associato viene passato come parametro this implicito (Me in Visual Basic). Se il metodo è statico, l'oggetto viene passato come primo parametro formale del metodo e la firma del delegato deve corrispondere ai parametri rimanenti. Per ulteriori informazioni e un codice di esempio, vedere System.Delegate.

Tutti i delegati ereditano da System.MulticastDelegate, che eredita da System.Delegate. I linguaggi C#, Visual Basic e C++ non consentono l'ereditarietà da questi tipi. Forniscono invece parole chiave per la dichiarazione di delegati.

Dal momento che i delegati ereditano da MulticastDelegate, un delegato presenta un elenco chiamate, ovvero un elenco di metodi rappresentati dal delegato che vengono eseguiti quando il delegato viene chiamato. Tutti i metodi dell'elenco ricevono gli argomenti forniti quando viene chiamato il delegato.

NotaNota

Il valore restituito non è definito per un delegato il cui elenco chiamate contiene più metodi, anche se il delegato dispone di un tipo restituito.

In molti casi, ad esempio con i metodi di callback, un delegato rappresenta un solo metodo e le uniche azioni che è necessario eseguire sono la creazione e il richiamo del delegato.

Per i delegati che rappresentano più metodi, .NET Framework fornisce metodi delle classi di delegati Delegate e MulticastDelegate per supportare operazioni quali l'aggiunta di un metodo a un elenco chiamate di un delegato (il metodo Delegate.Combine), la rimozione di un metodo (il metodo Delegate.Remove) e il recupero dell'elenco chiamate (il metodo Delegate.GetInvocationList).

NotaNota

Non è necessario utilizzare questi metodi per i delegati dei gestori di eventi nei linguaggi C#, C++ e Visual Basic perché questi linguaggi forniscono la sintassi per l'aggiunta e la rimozione dei gestori di eventi.

Torna all'inizio

Definizioni dei tipi

Una definizione di tipo include gli elementi seguenti:

  • Gli eventuali attributi definiti per il tipo.

  • Accessibilità del tipo (visibilità)

  • Il nome del tipo.

  • Il tipo base del tipo.

  • Le interfacce eventualmente implementate dal tipo.

  • Le definizioni per ciascuno dei membri del tipo.

Attributi

Gli attributi forniscono metadati aggiuntivi definiti dall'utente. Comunemente vengono utilizzati per archiviare informazioni aggiuntive su un tipo nell'assembly o per modificare il comportamento di un membro del tipo nella fase di progettazione o nell'ambiente di runtime.

Gli attributi stessi sono classi che ereditano da System.Attribute. I linguaggi che supportano l'utilizzo di attributi forniscono ognuno la sintassi necessaria per l'applicazione degli attributi a un elemento del linguaggio. Gli attributi possono essere applicati a quasi tutti gli elementi del linguaggio. Gli elementi specifici a cui è possibile applicare un attributo sono definiti dall'oggetto AttributeUsageAttribute applicato a tale classe di attributi.

Accessibilità dei tipi

Tutti i tipi dispongono di un modificatore che ne regola l'accessibilità da parte di altri tipi. Nella tabella che segue si descrive l'accessibilità dei tipi supportata dal runtime.

Accessibilità

Descrizione

public

Il tipo è accessibile da tutti gli assembly.

assembly

Il tipo è accessibile solo dall'interno dell'assembly.

L'accessibilità di un tipo annidato dipende dal relativo dominio di accessibilità, che è determinato sia dall'accessibilità dichiarata del membro che dal dominio di accessibilità del tipo che lo contiene direttamente. Tuttavia il dominio di accessibilità di un tipo annidato non può essere superiore a quello del tipo che lo contiene.

Il dominio di accessibilità di un membro annidato M dichiarato in un tipo T all'interno di un programma P viene definito come riportato di seguito (si noti che M potrebbe essere un tipo):

  • Se l'accessibilità dichiarata di M è public, il dominio di accessibilità di M è il dominio di accessibilità di T.

  • Se l'accessibilità dichiarata di M è protected internal, il dominio di accessibilità di M è l'intersezione del dominio di accessibilità di T con il testo di programma di P e il testo di programma di qualsiasi tipo derivato da T dichiarato esternamente a P.

  • Se l'accessibilità dichiarata di M è protected, il dominio di accessibilità di M è l'intersezione del dominio di accessibilità di T con il testo di programma di T e qualsiasi tipo derivato da T.

  • Se l'accessibilità dichiarata di M è internal, il dominio di accessibilità di M è l'intersezione del dominio di accessibilità di T con il testo di programma di P.

  • Se l'accessibilità dichiarata di M è private, il dominio di accessibilità di M è il testo di programma di T.

Nomi dei tipi

Il sistema di tipi comuni impone solo due restrizioni sui nomi:

  • Tutti i nomi sono codificati come stringhe di caratteri Unicode, ovvero a 16 bit.

  • I nomi non possono avere un valore incorporato (a 16 bit) pari a 0x0000.

La maggior parte dei linguaggi impone tuttavia restrizioni aggiuntive per i nomi dei tipi. Poiché tutti i confronti vengono effettuati byte per byte, sono indipendenti dalle impostazioni locali e fanno distinzione tra maiuscole e minuscole.

Sebbene un tipo possa fare riferimento a tipi di altri moduli e assembly, deve essere definito completamente all'interno di un modulo .NET Framework. A seconda del supporto del compilatore, tuttavia, può essere diviso in più file di codice sorgente. I nomi dei tipi devono essere univoci solo all'interno di uno spazio dei nomi. Per identificare completamente un tipo, il relativo nome deve essere qualificato dallo spazio dei nomi che contiene l'implementazione del tipo.

Tipi base e interfacce

Un tipo può ereditare valori e comportamenti da un altro tipo. Common Type System non consente ai tipi di ereditare da più di un tipo base.

Un tipo può implementare un numero indefinito di interfacce. Per implementare un'interfaccia un tipo deve implementare tutti i membri virtuali di tale interfaccia. Un metodo virtuale può essere implementato da un tipo derivato e può essere richiamato in modo statico o dinamico.

Torna all'inizio

Membri dei tipi

Il runtime consente di definire membri del tipo per specificare il comportamento e lo stato di un tipo. I membri dei tipi includono gli elementi seguenti:

  • Campi

  • Proprietà

  • Metodi

  • Costruttori

  • Eventi

  • Tipi annidati

Campi

Un campo descrive e contiene parte dello stato del tipo. I campi possono essere di qualsiasi tipo supportato dal runtime. Nella maggior parte dei casi, i campi sono private o protected, in modo che siano accessibili solo dall'interno della classe o da una classe derivata. Se il valore di un campo può essere modificato dall'esterno del relativo tipo, viene in genere utilizzata una funzione di accesso set della proprietà. I campi esposti pubblicamente sono in genere di sola lettura e possono essere di due tipi:

  • Costanti, il cui valore viene assegnato durante la fase di progettazione. Si tratta di membri statici di una classe, sebbene non vengano definiti utilizzando la parola chiave static (Shared in Visual Basic).

  • Variabili di sola lettura, i cui valori possono essere assegnati nel costruttore di classi.

Nell'esempio seguente vengono illustrati questi due utilizzi di campi di sola lettura.

Public Class Constants
   Public Const Pi As Double = 3.1416
   Public ReadOnly BirthDate As Date

   Public Sub New(birthDate As Date)
      Me.BirthDate = birthDate
   End Sub
End Class

Public Module Example
   Public Sub Main()
      Dim con As New Constants(#8/18/1974#)
      Console.WriteLine(Constants.Pi.ToString())
      Console.WriteLine(con.BirthDate.ToString("d"))
   End Sub
End Module
' The example displays the following output if run on a system whose current
' culture is en-US:
'    3.1417
'    8/18/1974
using System;

public class Constants
{
   public const double Pi = 3.1416;
   public readonly DateTime BirthDate;

   public Constants(DateTime birthDate)
   {
      this.BirthDate = birthDate;
   }
}

public class Example
{
   public static void Main()
   {
      Constants con = new Constants(new DateTime(1974, 8, 18));
      Console.Write(Constants.Pi + "\n");
      Console.Write(con.BirthDate.ToString("d") + "\n");
   }
}
// The example displays the following output if run on a system whose current
// culture is en-US:
//    3.1417
//    8/18/1974

Proprietà

Una proprietà consente di denominare un valore o uno stato del tipo e di definire i metodi per ottenere o impostare il valore della proprietà. Le proprietà possono essere tipi primitivi, insiemi di tipi primitivi, tipi definiti dall'utente, oppure insiemi di tipi definiti dall'utente. Sono spesso utilizzate per mantenere l'interfaccia pubblica di un tipo indipendente dalla sua effettiva rappresentazione. In questo modo le proprietà riflettono valori che non sono archiviati direttamente nella classe, ad esempio quando una proprietà restituisce un valore calcolato, oppure eseguono la convalida prima che i valori vengano assegnati a campi privati. Nell'esempio seguente viene illustrato quest'ultimo caso.

Public Class Person
   Private m_Age As Integer

   Public Property Age As Integer
      Get
         Return m_Age
      End Get
      Set
         If value < 0 Or value > 125 Then
            Throw New ArgumentOutOfRangeException("The value of the Age property must be between 0 and 125.")
         Else
            m_Age = value
         End If
      End Set
   End Property
End Class
using System;

public class Person
{
   private int m_Age;

   public int Age
   { 
      get { return m_Age; }
      set {
         if (value < 0 || value > 125)
         {
            throw new ArgumentOutOfRangeException("The value of the Age property must be between 0 and 125.");
         }
         else
         {
            m_Age = value;
         }         
      }
   }
}

Oltre a includere la proprietà stessa, il codice Microsoft Intermediate Language (MSIL) per un tipo che contiene una proprietà leggibile include un metodo get_nomeproprietà, mentre il codice MSIL per un tipo che contiene una proprietà scrivibile include un metodo set_nomeproprietà.

Metodi

Un metodo descrive le operazioni disponibili sul tipo. Nella firma di un metodo sono specificati i tipi consentiti di tutti i relativi parametri e del relativo valore restituito.

Sebbene molti metodi definiscono il numero preciso di parametri necessari per le chiamate al metodo, alcuni metodi supportano un numero di parametri variabile. Il parametro finale dichiarato di questi metodi è contrassegnato con l'attributo ParamArrayAttribute. I compilatori di linguaggio forniscono in genere una parola chiave, ad esempio params in C# e ParamArray in Visual Basic, che utilizza esplicitamente l'attributo ParamArrayAttribute non necessario.

Costruttori

Un costruttore è un tipo speciale di metodo che consente di creare nuove istanze di una classe o di una struttura. Analogamente a ogni altro metodo, un costruttore può includere parametri. I costruttori, tuttavia, non restituiscono alcun valore, ovvero restituiscono void.

Se il codice sorgente per una classe non definisce in modo esplicito un costruttore, il compilatore include un costruttore predefinito (senza parametri). Se tuttavia il codice sorgente per una classe definisce solo costruttori con parametri, i compilatori Visual Basic e C# non generano un costruttore senza parametri.

Se il codice sorgente per una struttura definisce i costruttori, essi devono essere con parametri. Una struttura non può definire un costruttore predefinito (senza parametri) e i compilatori non generano costruttori senza parametri per strutture o altri tipi di valori. Tutti i tipi di valori hanno un costruttore predefinito implicito. Questo costruttore viene implementato da Common Language Runtime e inizializza tutti i campi della struttura sui valori predefiniti.

Eventi

Un evento definisce una situazione a cui è possibile fornire risposta e metodi per la sottoscrizione, l'annullamento della sottoscrizione e la generazione dell'evento. Gli eventi sono spesso utilizzati per indicare modifiche di stato a tipi diversi. Per ulteriori informazioni, vedere Gestione e generazione di eventi.

Tipi annidati

I tipi annidati sono tipi membri di altri tipi. I tipi annidati devono essere strettamente collegati ai rispettivi tipi contenitori e non devono essere utilizzati come tipi generici. I tipi annidati risultano utili quando il tipo dichiarante utilizza e crea istanze del tipo annidato e quando il loro utilizzo non viene esposto in membri pubblici.

Per alcuni sviluppatori i tipi annidati possono generare confusione e dovrebbero essere visibili pubblicamente solo in casi di assoluta necessità. In una libreria progettata correttamente è improbabile che gli sviluppatori debbano utilizzare tipi annidati per creare istanze di oggetti o dichiarare variabili.

Torna all'inizio

Caratteristiche dei membri dei tipi

In Common Type System i membri dei tipi possono disporre di caratteristiche diverse, anche se non è necessario che i linguaggi le supportino tutte. Nella tabella riportata di seguito vengono descritte le caratteristiche dei membri.

Caratteristica

Si applica a

Descrizione

abstract

Metodi, proprietà ed eventi

Il tipo non fornisce l'implementazione del metodo. I tipi che ereditano o implementano metodi astratti devono fornire un'implementazione per il metodo. L'unica eccezione si verifica nel caso in cui il tipo derivato sia esso stesso un tipo astratto. Tutti i metodi astratti sono virtuali.

private, gruppo, assembly, gruppo e assembly, gruppo o assembly o public

Tutto

Consente di definire l'accessibilità del membro.

private

Accessibile solo dall'interno dello stesso tipo del membro o di un tipo annidato.

family

Accessibile dall'interno dello stesso tipo del membro e dai tipi derivati che ereditano da esso.

assembly

Accessibile solo nell'assembly nel quale il tipo viene definito.

family e assembly

Accessibile solo dai tipi che sono qualificati sia per l'accesso di gruppo che di assembly.

family o assembly

Accessibile solo dai tipi che sono qualificati per l'accesso di gruppo o per quello di assembly.

public

Accessibile da qualsiasi tipo.

final

Metodi, proprietà ed eventi

Il metodo virtuale non può essere sottoposto a override in un tipo derivato.

initialize-only

Campi

Il valore può essere solo inizializzato e non può essere scritto dopo l'inizializzazione.

instance

Campi, metodi, proprietà ed eventi

Se un membro non è contrassegnato come static (C# e C++), Shared (Visual Basic), virtual (C# e C++) o Overridable (Visual Basic), è un membro di istanza (nessuna parola chiave di istanza). Ci saranno tante copie di tali membri in memoria quanti sono gli oggetti che li utilizzano.

literal

Campi

Il valore assegnato al campo è un valore fisso, noto in fase di compilazione, di un tipo di valore incorporato. Ai campi literal viene a volte fatto riferimento come a costanti.

newslot o override

Tutti

Definisce il modo in cui il membro interagisce con membri ereditati aventi la stessa firma:

newslot

Nasconde i membri ereditati aventi la stessa firma.

override

Sostituisce la definizione di un metodo virtuale ereditato.

Il valore predefinito è newslot.

static

Campi, metodi, proprietà ed eventi

Il membro appartiene al tipo sul quale viene definito, non a un'istanza specifica del tipo. Il membro esiste anche se non viene creata un'istanza del tipo ed è condiviso tra tutte le istanze del tipo.

virtual

Metodi, proprietà ed eventi

Il metodo può essere implementato da un tipo derivato e può essere richiamato in modo statico o dinamico. Se viene utilizzata la chiamata dinamica, il tipo dell'istanza che effettua la chiamata in fase di esecuzione, e non il tipo noto in fase di compilazione, determina l'implementazione del metodo chiamata. Per richiamare un metodo virtuale in modo statico, potrebbe essere necessario eseguire il cast della variabile in un tipo che utilizza la versione desiderata del metodo.

Overload

Ciascun membro di tipo dispone di una firma univoca. La firma di un metodo è costituita dal nome del metodo e da un elenco di parametri, ovvero l'ordine e i tipi degli argomenti del metodo. All'interno di un tipo è possibile definire più metodi con lo stesso nome, purché la relativa firma sia diversa. Quando vengono definiti due o più metodi con lo stesso nome si dice che il metodo è stato sottoposto a overload. In System.Char, ad esempio, il metodo IsDigit è sottoposto a overload. Un metodo accetta un oggetto Char. L'altro metodo accetta un oggetto String e un oggetto Int32.

NotaNota

Il tipo restituito non viene considerato parte della firma del metodo.Ciò significa che i metodi non possono essere sottoposti a overload se differiscono unicamente per il tipo restituito.

Eredità, override e membri nascosti

Un tipo derivato eredita tutti i membri del relativo tipo di base, ovvero tali membri vengono definiti e resi disponibili nel tipo derivato. Il comportamento o le qualità dei membri ereditati possono essere modificati in due modi:

  • È possibile nascondere un membro ereditato con un tipo derivato definendo un nuovo membro con la stessa firma. Questa operazione può essere eseguita per rendere privato un membro precedentemente pubblico oppure per definire un nuovo comportamento per un metodo ereditato contrassegnato come final.

  • Con un tipo derivato è possibile eseguire l'override di un metodo virtuale ereditato. Nel metodo con cui viene eseguito l'override si fornisce una nuova definizione del metodo che sarà richiamato in base al tipo del valore in fase di esecuzione anziché in base al tipo della variabile nota in fase di compilazione. Un metodo può sottoporre a override un metodo virtuale solo se quest'ultimo non è contrassegnato come final e se il nuovo metodo è accessibile almeno quanto il metodo virtuale.

Torna all'inizio

Vedere anche

Concetti

Common Language Runtime (CLR)

Altre risorse

Riferimento a .NET Framework