Login Login
MORE

WIDGETS

Widgets

Wanted articles
Who is online?
Article tools

CSharp:Info fondamentali

From Aino Wiki

Jump to: navigation, search

Contents

Linguaggio

Ci sono conoscenze di base, fondamentali e poi propedeutiche per un minimo di professionalità.
Ogni corso di specializzazione assumerà la conoscenza della programmazione OOP (Object Oriented programming). Occorre conoscere l'implementazione delle Interfaccie, i tre pilastri dell'OOP: Astrazione, Ereditarietà e Polimorfismo; i principi SOLID, la differenza tra Composizione e Ereditarietà, i diagrammi delle classi.

Tipi

Boolean    = 4 byte                                      bit
Byte       = 1 byte                                      TinyInt
Char       = 2 byte
Date       = 8 byte                                      DateTime
Decimal    = 12 byte  Numeri con la virgola + e -                  (Da -7,9 x 10^28 a 7,9 x 10^28) / (da 10^(0 a 28))
Double     = 8 byte   Numeri con la virgola + e -        Money
Integer    = 4 byte = Int32                              Int
Long       = 8 byte = Int64			          BigInt
Object     = 4 byte
Short      = 2 byte = Int16                              SmalInt
Single     = 4 byte   Numeri con la virgola + e -        SmallMoney
String     = 10 byte + (2 *lunghezza stringa) Caratteri  UniCode

Stringhe

string variabileTipoStringa = string.empty;
int variabileTipoIntero = 123;
 
// Assegnazione semplice
variabileTipoStringa = "ciccio" + " " + "cappccio ha " + variabileTipoIntero.ToString + "\n";
 
// Assegnazione formattata
variabileTipoStringa = string.Format("{0} {1} ha {2}", ciccio", "cappccio", variabileTipoIntero);
 
// Assegnazione con caratteri di controllo e speciali, uso della @
variabileTipoStringa = @"rigo uno
rigo due
rigo tre";
variabileTipoStringa = @"rigo uno\r\nrigo due\r\nrigo tre";
string variabileTipoStringa = string.empty;
int variabileTipoIntero = 123;
private class Persona
{
    public string Nome => "Nome, attributo di default";
    public string Cognome {get; set;}
}
Persona tizio = new Persona()
{
   Cognome = "zighine"
};
// Assegnazione con stampa di valori da variabili in questo ambito\scope, uso della $ ad inizio stringa e {}
variabileTipoStringa = $"Numero {variabileTipoIntero}.";
variabileTipoStringa = $"{tizio.Nome} ha il numero {variabileTipoIntero}.";

Date

Data minima o massima

Per valorizzare la data con un valore di riferimento si può usare la minima rappresentazione del compilatore, usando la funzione DateTime.MinValue.

DateTime sourceTimeStamp = DateTime.MinValue;

Min date e Max date

Consigli

Un cosniglio, se questi valori finiscono poi sul DB converrebbe usare un valore standard compatibile con la rappresentazione sul DB e quindi se questo è MS SQL Server: SqlDateTime.MinValue.Value. Questo preverrà eccezioni nel casting delle date.

Per usare questo valore occorre usare un riferimento ad una libreria specifica e lo using System.Data.SqlTypes;.
using System.Data.SqlTypes;
//..
DateTime sourceTimeStamp = SqlDateTime.MinValue.Value;
Stesso dicasi per il valore massimo delle date rappresentabili.

Altro esempio:

using System.Data.SqlTypes;
//..
List<TipologiaPiatti> elencoGruppiPiatti = new List<TipologiaPiatti>();
 
rotazioniItChef.Add(new RotazioniAttiveData()
{
  id_rotazione = 1,
  revisione_rotazione = 1,
  descrizione_rotazione = TipoImpiantoEnum.CUCE.ToString(),
  durata_rotazione = 28,
  giorno_inizio_rotazione = 1,  // Lunedì
  giorno_fine_rotazione = 0,    // Domenica
  data_inizio_rotazione = SqlDateTime.MinValue.Value.ToString("dd/MM/yyyy"),
  data_fine_rotazione = SqlDateTime.MaxValue.Value.ToString("dd/MM/yyyy"),
});

Enumerativi

Segue esempio con l'aggiunta anche della "descrizione", attributo utile non solo per commentare un enumerativo ma anche per usarlo al posto del nome degli item quando non sono descrittivi ed inserirli es. in DropDownList.
NOTA per usre l'attributo di descrizione è necessario aggiungere la USING 'System.ComponentModel':

        public enum SearchModeEnum
        {
            [Description("%")]
            LIKE = 0,
            [Description("=")]
            EQUAL = 1
        }
 
        public enum SortDirectionEnum
        {
            [Description("Ascendente")]
            ASC = 0,
            [Description("Discendente")]
            DESC = 1
        }

Variabili

Valorizzare direttamente tipizzando

Tipi di valore. Ecco come scrivere un valore decimale in una variabile:

decimal amount = 10.01M;
// che è meglio di scrivere:
decimal amount1 = decimal.Parse("10.02");

Ecco come valorizzare una variabile di tipo float:

float reliability = 1.0f;

Segue tabella di conversione:

Tipo carattere
decimal M
float F
int --default--
uint U
long L

Ambito \ scope

Da MicrosoftLe variabili in un oggetto posson essere dichiarate con prefisso di ambito (scoping terms): Private, Public, Protected, Friend, ProtectedFriend
ma se non viene indicato il prefisso di ambito, quindi col default scope, esse sono Private, invece è Internal (visibili solo nello stesso assembly) per le strutture, classi, metodi e delegati !

Members of    Default member accessibility
----------    ----------------------------
enum          public
class         private
interface     public
struct        private

Array di oggetti

Segue utile esempio in cui si definisce e assegna un array di oggetti al volo in forma compatta.
        ShipPackage[] m_shipPackages = new ShipPackage[]
        {
            new ShipPackage { ShipFullCode = "C_AT", PackageFileNameFullPath = "D:\\KT\\C_AT_20160518_1619", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "C_CL", PackageFileNameFullPath = "", ErrorDescription = "E' scoppiato un capicazz" },
            new ShipPackage { ShipFullCode = "C_DE", PackageFileNameFullPath = "D:\\KT\\C_DE_20160518_1621", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "C_DI", PackageFileNameFullPath = "D:\\KT\\C_DI_20160518_1619", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "C_FA", PackageFileNameFullPath = "C:\\KT\\C_FA_20160518_1620", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "C_FS", PackageFileNameFullPath = "", ErrorDescription = "Sèèè e mo lo volevi no?" },
            new ShipPackage { ShipFullCode = "C_LU", PackageFileNameFullPath = "D:\\KT\\C_LU_20160518_1621", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "C_MD", PackageFileNameFullPath = "D:\\KT\\C_MD_20160518_1619", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "C_PA", PackageFileNameFullPath = "C:\\KT\\C_PA_20160518_1620", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "C_NR", PackageFileNameFullPath = "D:\\KT\\C_NR_20160518_1621", ErrorDescription = "" },
            // No Serena e Vittoria                                                                        
 
            new ShipPackage { ShipFullCode = "A_AU", PackageFileNameFullPath = "D:\\KT\\A_AU_20160518_1621", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "A_BE", PackageFileNameFullPath = "D:\\KT\\A_BE_20160518_1621", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "A_CA", PackageFileNameFullPath = "D:\\KT\\A_CA_20160518_1621", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "A_DV", PackageFileNameFullPath = "D:\\KT\\A_DV_20160518_1621", ErrorDescription = "" },
            new ShipPackage { ShipFullCode = "A_LU", PackageFileNameFullPath = "D:\\KT\\A_LU_20160518_1622", ErrorDescription = "" }
            //NO ETC
        };
 
// ... segue pezzo di codice per la ricerca mediante LinQ
var shipPackage = m_shipPackages.FirstOrDefault((p) => p.ShipFullCode == id);
 
// Oppure:
            ShipPackage[] arr_shipPackages;
            arr_shipPackages = m_shipPackages.Where((p) => p.ShipFullCode == id).ToArray<ShipPackage>();

Property e Attributi

Property:

int _number;
public int Number
{
	get
	{
		return this._number;
	}
	set
	{
		this._number = value;
	}
}

Property read only, potranno comunque essere valorizzate all'interno della classe come una qualsiasi variabile privata:

public static string PublicClientId
{
    get;
    private set;
}

Conversioni

ToString Coalesce expression

null coalesce expression ?.ToString()

private void businessAPINoah_BusinessAPIEvent(IBusinessAPIEventArg arg)
{
    log.InfoFormat("Init. arg = '{0}'", arg?.ToString() ?? "NULL");
    // ... etc

Funzioni matematiche

Ottenute con la libreria Math. Funzioni di matematica

Controllo del flusso

switch case

Una specie di multi if corrispondente all'istruzione switch case:
string strValue = "xxx";
switch (strValue)
{
	case "Caso 1":
	case "Caso 2":
	case "Caso 3":
		Console.Write("Caso 1,2,3");
		break;
	case "Caso 4":
		Console.Write("Caso 4, 6?");
		break;
	case "Caso 5":
		Console.Write("Caso 4");
		break;
	case "Caso 6":
                goto case "Caso 4"; // ATTENZIONE goto per condividere un caso
	case "Caso ok":
		return true;
	default:
		// Quando si verifica un caso imprevisto
		return false; // Esce dal Metodo con esito falso!
}
Compilazione automatica

L'interprete costruirà automaticamente il costrutto switch nel seguente modo. Iniziare a scrivere: sw, si apre il menu dell'intellisense, quindi premente il tasto TAB 2 volte, si costruirà automaticamente.
Se si vuol creare uno switch su un enumerativo si selezina (subito rispetto all'operazione precedente) la variabile e si preme TAB e poi la freccia in giù, si creeranno automaticamente tutti i case per tutti i valori enumerativi previsti.

Cicli

for

for (int i = 1; i <= 5; i++)
{
	Console.WriteLine(i);
}

foreach

Costrutto For each
foreach (GridViewRow gvrow in coll) // PER OGNI ship Position
{
	...
	break; // Esce dal foreach
	...
	continue; // Va all'item successivo
	...
	return; // Esce dalla procedure\function
}
È inoltre possibile uscire da un ciclo foreach mediante le istruzioni goto e throw.

While loop

int n = 1;
while (n < 6) 
{
     Console.WriteLine("Current value of n is {0}", n);
     n++;
     // break; // <--- esce dal ciclo while !
}

Do While Loop

int i = 0;
 
do
{
    //Operazioni con l'indice i
    // ... etc ...
    i++;
} while (i < 7);

Attributi di variabile

Multithreding

Da Microsoft
La parola chiave volatile indica che un campo potrebbe essere modificato da più thread in esecuzione contemporaneamente. I campi dichiarati volatile non sono soggetti a ottimizzazioni del compilatore che presuppongono l'accesso da un singolo thread. In questo modo nel campo è sempre presente il valore più aggiornato.
Il modificatore volatile è utilizzato in genere per un campo al quale accedono più thread senza ricorrere all'istruzione lock per la serializzazione dell'accesso.

Assembly

Estratto da giuseppesicari.it
Un assembly è una raccolta di tipi e risorse progettati per interagire fra loro formando unità funzionali.

Un assembly costituisce l’unità fondamentale per la creazione di applicazioni .NET in quanto svolge le seguenti funzioni:

  • contiene codice MSIL (Microsoft Intermediate Language)
  • definisce i vincoli di sicurezza ricevendo ed accogliendo le richieste di autorizzazione
  • contribuisce alla definizione dei tipi la cui identità include in nome dell’assembly di appartenenza
  • assicura l’integrità di versione costituendo la più piccola unità aggiornabile all’interno del Common Language Runtime
  • costituisce un’unità di distribuzione: all’avvio di un’applicazione gli unici assembly necessari sono appunto quelli richiesti dall’applicazione stessa per il suo funzionamento

Per risolvere i problemi di controllo delle versioni e di conflitto fra DLL si utilizzano assembly che:

  • consentono di specificare regole di versione
  • forniscono l’infrastruttura per l’attivazione delle regole il controllo delle versioni
  • forniscono l’infrastruttura per l’esecuzione contemporanea di differenti versioni di un componente

Struttura di un Assembly

Un assembly è costrituito da quattro elementi:

  • il manifesto che contiene i metadati dell’assembly
  • i metadati dei tipi utilizzati all’interno del codice MSIL
  • il codice MSIL vero e proprio
  • un insieme di risorse
CSharp struttura assembly.jpg

L’immagine su mostra la struttura tipica di un assembly su file singolo (Portable Executable).

L’organizzazione su file singolo è certamente quella per la quale optare quando si dispone di codice scritto nel medesimo linguaggio: è possibile esaminare la struttura interna di un assembly utilizzando l’utility ILDasm.exe fornita con il framework .NET.

Tali elementi possono essere raggruppati in un’unico file oppure essere distribuiti su più file: in quest’ultimo caso il manifesto dell’assembly conterrà tutti i riferimenti relativi ai file che costituiscono l’assembly stesso.

Lo stesso manifesto può essere memorizzato in un file Portable Executable (exe o dll) insieme al codice MSIL oppure in un Portable Executable autonomo.

Assembly su più file

CSharp assembly multifile.jpg
Generalmente si opta per un assembly distribuito su più file quando si vogliono mettere insieme moduli sviluppati in linguaggi differenti.

Il manifesto di un assembly contiene le seguenti informazioni:

  • nome dell’assembly
  • numero di versione
  • lingua
  • nome sicuro
  • elenco di tutti i file dell’assembly
  • informazioni per il riferimento ai tipi
  • informazioni sugli assembly a cui si fa riferimento

E’ possibile modificare o aggiungere informazioni al manifesto utilizzando gli attributi dell’assembly nel proprio codice.

Attributi relativi all’identità

Gli attributi relativi all’identità dell’assembly utilizzabili sono:

  • AssemblyCultureAttribute: permette di indicare la lingua supportata dall’assembly
  • AssemblyFlagsAttribute: permette di impostare gli attributi relativi all’assembly indicando ad esempio e è possibile l’esecuzione affiancata di più versioni
  • AssemblyVersionAttribute: permette di specificare la versione dell’assembly nel formato

<numero_principale>.<numero_secondario>.<numero_di_build>.<numero_di_revisione>

Attributi informativi

Gli attributi informativi permettono di specificare informazioni addizionali relative all’assembly:

  • AssemblyCompanyAttribute: permette di indicare il nome della società che ha prodotto l’assembly
  • AssemblyCopyrightAttribute: permette di specificare informazioni relative al copyright
  • AssemblyFileVersionAttribute: permette di specificare il numero di versione del file (l’impostazione predefinita corrisponde al numro di versione dell’assembly stesso)
  • AssemblyInformationalVersionAttribute: permette di specificare ulteriori informazioni relative alla versione
  • AssemblyProductAttribute: utilizzabile per specificare ulteriori informazioni relative al prodotto
  • AssemblyTrademarkAttribute: permette di specificare informazioni relative al marchio registrato

Attributi relativi al manifesto dell’assembly

I seguenti attributi permettono di fornire informazioni nel manifesto dell’assembly:

  • AssemblyConfigurationAttribute: indica la configurazione dell’assembly (ad esempio finale o debug)
  • AssemblyDefaultAliasAttribute: permette di specificare l’alias predefinito che verrà utilizzato da altri assembly come riferimento all’assembly corrente.
  • AsemblyDescriptionAttribute: contiene la descrizione dell’assembly stesso
  • AssemblyTitleAttribute: consente di specificare un nome descrittivo per l’assembly.

Attributi relativi al nome sicuro

Un nome sicuro è costituito dall’identità dell’assembly (nome semplice, numero di versione, informazioni sulla lingua) nonchè da una chiave pubblica ed una firma digitale.

Il nome sicuro viene generato a partire dal file dell’assembly che contiene il manifesto utilizzando una chiave privata.

I nomi sicuri soddisfano i seguenti requisiti:

  • garantiscono l’univocità dei nomi (in quanto basati su una coppia di chiavi univoche)
  • garantiscono la discendenza delle versioni di un assembly
  • garantiscono un conrtollo di integrità


I criteri di versione predefiniti del Common Language Runtime prevedono che l’applicazione venga eseguita soltanto con le versione con le quali è stata creata: è opportuno precisare che il controllo di versione viene effettuato soltanto sugli assembly con nome sicuro.

Gli attributi relativi al nome sicuro di un assembly sono:

  • AssemblyDelaySignAttribute: valore booleano che indica il ritardo della firma
  • AssemblyKeyFileAttribute: consente di indicare il nome del file contenete la chiave pubblica
  • AssemblyKeyNameAttribute: Indica il contenitore di chiave contenente la coppia di chiavi passata come parametro al costruttore dell’attributo.

Global Assembly Cache - GAC

Quando è necessario condividere un assembly fra più applicazioni è possibile inserirlo nella Global Assembly Cache (GAC) utilizzando un programma di installazione in grado di gestire la GAC oppure utilizzando lo strumento gacutil.exe fornito insieme al framework .NET.
Semplicemente: è localizzata in %windir%\assembly (es.: C:\WINDOWS\assembly) è un repository di librerie condiviso.

Accesso al DB

Stringa di connessione

Connection string presente nel web.config oppure app.config nell'apposita sezione.
Segue esempio semplice:
<?xml version="1.0"?>
<configuration>
  <connectionStrings>    
    <add name="DB_UNO" connectionString="Data Source=aino-cucu.pizza.net; Initial Catalog=RouteMonitoring;User Id=utente;Password=zippo;Integrated Security=False" providerName="System.Data.SqlClient"/>
  </connectionStrings>
</configuration>

Nel codice C# verrà usata come segue:

using System.Configuration;
...
// Usandola come segue
var connectionString=ConfigurationManager.ConnectionStrings["DB_UNO"].ConnectionString;

Es. con DB bilanciati

Segue esempio per accesso a 2 DB bilanciati, se uno non è in linea automaticamente si passa al secondo:

<?xml version="1.0"?>
<configuration>
  <connectionStrings>    
    <add name="DB1" connectionString="Data Source=aino-cucu.pizza.net,10000;Failover Partner=aino-cucu.pizza.net,10001;Initial Catalog=RouteMonitoring;User Id=utente;Password=zippo;Integrated Security=False" providerName="System.Data.SqlClient"/>
    <add name="DB2" connectionString="Data Source=aino-cucu.pizza.net,10000;Failover Partner=aino-cucu.pizza.net,10001;Initial Catalog=RouteMonitoring;User Id=neptune_writer;Password=zippo;Integrated Security=False" providerName="System.Data.SqlClient"/>
    <add name="DB3" connectionString="Data Source=aino-cucu.pizza.net,10000;Failover Partner=aino-cucu.pizza.net,10001;Initial Catalog=RouteMonitoring;User Id=neptune_writer;Password=zippo;Integrated Security=False" providerName="System.Data.SqlClient"/>
  </connectionStrings>
</configuration>

Altre info su DB blilanciati

Ex. Entity Framework Connection String

Si usa metadata, è usato obbligatoriamente in caso di EntityFramework connection string e specifica un pipe-delimited lista di directory, files, e risorse in cui cercare modelli e informazioni di mappatura.

<add name="xxxCCEntities" connectionString="metadata=res://*/App_Code.xxxCC.csdl|res://*/App_Code.xxxCC.ssdl|res://*/App_Code.xxxCC.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.\sqlexpress2012;initial catalog=xxxCC;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />


The metadata field is required in an EF connection string and specifies:

A pipe-delimited list of directories, files, and resource locations in which to look for model and mapping information. (source: http://msdn.microsoft.com/en-us/library/system.data.entityclient.entityconnection.connectionstring.aspx)

Or put in other words:

The pointer to the metadata files (Conceptual Schema Definition Layer [CSDL], Mapping Schema Layer [MSL], and Store Schema Definition Layer [SSDL]) (source: http://msdn.microsoft.com/en-us/library/orm-9780596520281-01-16.aspx)

ADO

DataTable result = null;
SqlCommand sqlcmd = new SqlCommand(query, connection);
sqlcmd.CommandTimeout = 120; // SECONDI !
SqlDataAdapter sqlda = new SqlDataAdapter(sqlcmd);
result = new DataTable();
sqlda.Fill(result);

E' il tempo di attesa in secondi per l'esecuzione del comando. Il valore predefinito è 30 secondi.

DataReader

Notare che se un campo non è selezionato in SELECT SQL, c'è la possibilità di valutare la sua presenza mediante funzione reader.FieldExists("nomeCampo")

List<Alarm> alarms = new List<Alarm>();
 
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandType = System.Data.CommandType.Text;
string qrySQL = @"SELECT 
				* FROM Alarms with(nolock)
				WHERE 1=1";
 
using (SqlDataReader reader = command.ExecuteReader())
{
	while (reader.Read())
	{
		Alarm alarm = new Alarm();
		//Stralcio di acquisizione		
 
		alarm.Acknowledged = (Boolean)reader["Acknowledged"];
		alarm.AcknowledgeDate = DateHelper.SpecifyUtcKind(reader["AcknowledgeDate"] as DateTime?);
		alarm.ShipCode = reader["ShipCode"] as string;
		alarm.Segment1Id = reader["Segment1Id"] as Guid?;
		alarm.GeoPosition = reader["GeoPosition"] as SqlGeometry;
		alarm.Id = (Guid)reader["Id"];
 
		// Metodo alternativo a sopra --^
		//--------------------------------------------------
		alarm.Acknowledged = reader["Acknowledged"].AsBool();
        alarm.ShipCode = reader["ShipCode"].AsString(),
        alarm.IntValueEx = reader["IntValueEx"].AsInt(),
        alarm.AcknowledgeDate = reader["AcknowledgeDate"].AsDateTime(),
        alarm.Id = reader["Id"].AsGuid(),
		//------- ATTENZIONE, esempio in caso la query sul DB non restituisca il campo indipendentemente dal suo contenuto
        alarm.CampoOPZIONALE = reader.FieldExists("CAMPO_NON_IN_SELECT") 
                                    ? reader["CAMPO_NON_IN_SELECT"].AsInt() 
                                    : -1,
		//--------------------------------------------------
 
		alarms.Add(alarm);
	}
}

Uso Stored Procedure

Quel che cambia è il parametro: CommandType.StoredProcedure. Caso Richiamo StoredProcedure din Inserimento:

using (SqlConnection sqlConn = new SqlConnection(connectionString)) {
	using (SqlCommand cmd = new SqlCommand("sp_Add_contact", sqlConn)) {
	  cmd.CommandType = CommandType.StoredProcedure;
 
	  cmd.Parameters.Add("@FirstName", SqlDbType.VarChar).Value = txtFirstName.Text;
	  cmd.Parameters.Add("@LastName", SqlDbType.VarChar).Value = txtLastName.Text;
 
	  sqlConn.Open();
	  cmd.ExecuteNonQuery();
          sqlConn.Close();
	}
}

Verifica dei tipi

Per SQL Server

Usando la DLL corrispondente allo spazio dei nomi "Microsoft.SqlServer.Types" si riusciranno a fare delle verifiche sul tipo di parametri\oggetti inviati come valore per l'esecuzione di query SQL

        /// <summary>
        /// Crea una lista di SqlParameter da un semplice Dictionary. Ogni parametro avrà direzione input.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        private static List<SqlParameter> SetupParameters(Dictionary<string, object> parameters)
        {
            List<SqlParameter> result = new List<SqlParameter>();
 
            if (parameters != null && parameters.Count > 0)
            {
                foreach (String parName in parameters.Keys)
                {
                    Object parValue = null;
                    SqlParameter sqlpar = null;
 
                    sqlpar = new SqlParameter();
                    sqlpar.ParameterName = parName;
                    sqlpar.Direction = ParameterDirection.Input;
                    parValue = parameters[parName];
 
                    if (parValue != null)
                    {
                        sqlpar.Value = parValue;
                        if (parValue.GetType() == typeof(SqlGeometry))
                        {
                            sqlpar.SqlDbType = System.Data.SqlDbType.Udt;
                            sqlpar.UdtTypeName = "geometry";
                        }
                    }
                    else 
                       sqlpar.Value = DBNull.Value;
 
                    result.Add(sqlpar);
                }
            }
            return result;
        }

Ci sono due (attualmente) versioni della DLL, ATTENZIONE a non sbagliare perchè la riga:

if (parValue.GetType() == typeof(SqlGeometry))

non funzionerebbe. La versione a cui si riferisce questo esempio è la:

C:\Program Files (x86)\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Types.dll

Entity Framework

Abbreviato anche EF.
Svantaggi: social.msdn.microsoft.com

L’accesso ai dati avviene attraverso l’Entity Framework che anche se giova in fase di sviluppo, in termini di tempi, appesantisce in fase di manutenzione. Astraendo la base dati, impone che ogni modifica strutturale sia riverberata su entrambe i livelli, occorre fare attenzione a tenere allineati entrambi i livelli, sovente è causa di aggiornamenti di versione non voluti complicando la diagnosi di problemi. Altra obiezione è la scarsa scalabilità (The number in a Microsoft screencast is a maximum of roughly 250 tables per EF model).

Unfortunately, the EF queries are generated by the provider that we cannot control. For some specified query that you want to customize, you can try DataContext.ExecuteQuery<TResult>(string query, params object[] parameters).

Approfondimento qui: CSharp:EntityFramework

Ereditarietà

Crea 3 classi, una padre e due specializzazioni ovvero classi derivate. Dato un enumerativo passato ad una funzione crea una istanza di una classe derivata assegnando opportuni valori. Cambiando l'enumerativo la classe ed i valori assegnati cambieranno.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
 
namespace TestEreditarieta
{
    class Program
    {
        #region Classi della famiglia
        public class Cls_A_Padre
        {
            // La seguente Property potrebbe essere omessa ma E' UTILISSIMA per 
            // DISCERNERE DIRETTAMENTE IL TIPO DELLA CLASSE ESTESA !!!
            public EnumTipo flagTipo
            {
                get;
                set;
            }
            public string A_Uno
            {
                get;
                set;
            }
            public string A_Due
            {
                get;
                set;
            }
        }
        public class Cls_B_Figlio : Cls_A_Padre
        {
            public string B_Uno
            {
                get;
                set;
            }
            public string B_Due
            {
                get;
                set;
            }
        }
        public class Cls_C_Figlio : Cls_A_Padre
        {
            public string C_Uno
            {
                get;
                set;
            }
            public string C_Due
            {
                get;
                set;
            }
        }
        #endregion
 
        public enum EnumTipo
        {
            B = 0,
            C = 1
        }
 
        static void Main(string[] args)
        {
            Cls_A_Padre generica = Inizializza(EnumTipo.C);
 
            Type myObjectType = generica.GetType(); // typeof(generica);
 
            IList<PropertyInfo> props = new List<PropertyInfo>(myObjectType.GetProperties());
 
            foreach (PropertyInfo prop in props)
            {
                object propValue = prop.GetValue(generica, null);
 
                Console.WriteLine("Valore = {0}, tipo {1}", propValue.ToString(), propValue.GetType());
            }
 
            Console.ReadLine();
        }
 
        private static Cls_A_Padre Inizializza(EnumTipo flagTipo)
        {
            Cls_A_Padre objIstanziato = new Cls_A_Padre();
            objIstanziato.A_Uno = "1aa";
            objIstanziato.A_Due = "2aa";
 
            switch (flagTipo)
            {
                case EnumTipo.B:
                    Cls_B_Figlio objB = new Cls_B_Figlio();
                    objB.A_Uno = "1a";
                    objB.A_Due = "2a";
                    objB.B_Uno = "1b";
                    objB.B_Due = "2b";
                    objIstanziato = objB;
                    break;
                case EnumTipo.C:
                    Cls_C_Figlio objC = new Cls_C_Figlio();
                    objC.A_Uno = "1a";
                    objC.A_Due = "2a";
                    objC.C_Uno = "1c";
                    objC.C_Due = "2c";
                    objIstanziato = objC;
                    break;
                default:
                    break;
            }
 
 
            return objIstanziato;
        }
    }
}

Valori visualizzati:
Valore = 1c, tipo System.String
Valore = 2c, tipo System.String
Valore = 1a, tipo System.String
Valore = 2a, tipo System.String

Interfacce

(Da http://www.html.it/pag/15450/le-interfacce/ HTML.it])Un’interfaccia è un gruppo astratto di membri, un contratto: chi implementa una interfaccia si impegna a scrivere il codice per ciascun metodo.

public interface IEsempio
{
  void Metodo1();
  bool Metodo2();
 
  string Proprieta1 { get; set; }
  int Proprieta2 { get; }
}

I nomi delle interfacce dovrebbe iniziare con la lettera I maiuscola. Tutto ciò che è dichiarato all’interno di una interfaccia è pubblico (ed è un errore indicare il modificatore public).

Un’interfaccia non può contenere variabili, struct, enum e, come già accennato, implementazioni di metodi (tutte cose che, invece, sono consentite nelle classi astratte).

Un’interfaccia viene implementata da una classe. Per indicare che una classe implementa un’interfaccia, in C# si usa il carattere ":", lo stesso con cui si esprime l’ereditarietà tra classi (in VB.NET si usa la parola chiave Implements):

public class Esempio : IEsempio
{
  public void Metodo1()
  {
    //...
  }
 
  public bool Metodo2() { return true; }
 
  public string Proprieta1
  {
    get { return string.Empty; }
    set { /*...*/ }
  }
 
  public int Proprieta2
  {
    get { return 0; }
  }
}

La classe che implementa un’interfaccia deve fornire l’implementazione di tutti i suoi metodi e delle sue proprietà, in questo caso però non c’è bisogno della parola chiave override, è sufficiente che i metodi definiti abbiano lo stesso nome con cui sono dichiarati nell’interfaccia.

In VB.NET, invece, i nomi assegnati ai metodi non sono vincolanti, poiché il metodo dell’interfaccia che si sta implementando è indicato esplicitamente.

Come già visto in precedenza, una classe può ereditare da una sola classe base, mentre può implementare un numero illimitato di interfacce:

public class Studente : Persona, IEsempio1, IEsempio2

Classi Astratte

Una classe astratta è una classe che non può essere usata direttamente ovvero non può essere istanziata con la parola chiave new ma deve essere obbligatoriamente ereditata.
Le classi astratte permetto d'implementare il concetto di polimorfismo consentendo di assegnare comportamenti differenti a instanze di classi differenti derivanti tutte dalla stessa classe base. I metodi e le proprietà del tipo base devono essere dichiarati come astratti o virtuali affinchè sia attuato il polimorfismo.
Altra cosa come si vedrà più sotto, sostanzialmente le classi astratte consentono di "fattorizzazione" delle informazioni comuni da non dover ripetere in tutte le classi che sono derivate.

public abstract class Figura
{
  // Attribti
  protected string m_Nome;
  protected int m_NumeroLati;
 
  // Costruttore
  public Figura(string nome, int numeroLati)
  {
    m_Nome = nome;
    m_NumeroLati = numeroLati;
  }
 
 
  #region Properties
  public string Nome
  {
    get 
     { return m_Nome; }
  }
 
  public int NumeroLati 
  { 
    get 
     { return m_NumeroLati; }
  }
 
//Properties astratte
  public abstract double Perimetro 
   { get; }
  public abstract double Area 
   { get; }
  #endregion
 
  #region Metodi
  // Eventuali metodi con azioni sicuramente comuni a tutte le classi che saranno derivate
 
  // Metodi ASTRATTI
  public abstract void Disegna(); // <-- notare che i metodi astratti non prevedono implementazione
  #region
}

I metodi e le proprietà definite abstract DEVONO essere implementati nelle classi che ereditano dalla nostra classe astratta.
Vere seguente esempio di classe che eredita da "Figura".

public class Quadrato : Figura
{
  private double m_Lato;
 
  // Costruttore che eredita dal costruttore base ed aggiunge un argomento
  public Quadrato(int lato) : base("Quadrato", 4)
  {
    m_Lato = lato;
  }
 
  // Proprietà (definite nella classe astratta) che DEVONO essere sovrascritte
  public override double Perimetro
  {
    get { return m_Lato * 4; }
  }
 
  public override double Area
  {
    get { return m_Lato * m_Lato; }
  }
 
  // Metodo che DEVE essere sovrascritto
  public override void Disegna()
  {
    //…
  }
}

Metodi

Introduzione

ToDo ...

Casi e sintassi particolari

(=>) Lambda expression e expression-bodied members

expression-bodied
C'è un modo contratto per definire subito (in una riga) il risultato in output di un metodo, usando => (Expression bodies function members introdotto con C# 6):

public Point Move(int dx, int dy) => new Point(x + dx, y + dy); 
 
public void Print() => Console.WriteLine(First + " " + Last); 
// ^-- Works with operators, properties, and indexers too. public static Complex operator 
// +(Complex a, Complex b) => a.Add(b); public string Name => First + " " + Last; 
// public Customer this[long id] => store.LookupCustomer(id);

Metodi Virtuali

Un metodo virtuale (Virtual) è un metodo che può essere ridefinito (sovrascritto, overwritable). Utile quando si deriva da una classe si ha la possibilità di sovrascrivere il metodo virtuale così da adattarlo alle azioni richieste nella nuova classe altrimenti si può conservare il metodo virtuale che evidentemente ha un comportamento generico.

Metodi Astratto

Un metodo astratto (abstract) non ha una implementazione quindi in una classe derivata va definito!

Metodi anonimi

vedi qui: [1]
I metodi anonimi forniscono una tecnica per passare un blocco di codice come paramero delegato. i metodi anonimi sono i metodi senza un nome, solo il corpo

Es 1

Using System;
 
delegate void CambiaNumero(int n);
namespace DelegateAppl
{
   class TestDelegate
   {
      static int num = 10; // Variabile STATICA su cui agiranno TUTTI i metodi delegati nominativi e non
      public static void AddNum(int p)
      {
         num += p;
         Console.WriteLine("Metodo nominativo 'AddNum': {0}", num);
      }
 
      public static void MultNum(int q)
      {
         num *= q;
         Console.WriteLine("Metodo nominativo 'MultNum': {0}", num);
      }
 
      public static int getNum()
      {
         return num;
      }
      static void Main(string[] args)
      {
         // Crea un istanza del delegato usando il seguente Metodo ANONIMO
         CambiaNumero istanzaMetodiDelegatiCN = delegate(int x)
         {
            Console.WriteLine("Metodo anonimo: {0}", x);
         };
 
         istanzaMetodiDelegatiCN(10); // Chiama il delegato usando il metodo anonimo di prima
 
         istanzaMetodiDelegatiCN =  new CambiaNumero(AddNum); // Istanzia il Delegato passandogli il metodo nominativo da eseguire
 
         istanzaMetodiDelegatiCN(5); // Esegue il delegato usando il metodo nominativo 
 
         istanzaMetodiDelegatiCN =  new CambiaNumero(MultNum); //Istanzia il Delegato usando un altro il metodo nominativo 
 
         istanzaMetodiDelegatiCN(2); // Esegue il delegato usando il metodo nominativo 
         Console.ReadKey();
      }
   }
}
Che produrrà:
Metodo anonimo: 10
Metodo nominativo 'AddNum': 15
Metodo nominativo 'MultNum': 30

Es 2

 
 

Oggetti Dinamici

Expand Object

Introdotti con C# 4
blogs.msdn.microsoft Sono oggetti costruibili dinamicamente un pò come avviene in JavaScript e Json.
Esempi di dichiarazione:

dynamic contact = new ExpandoObject();
contact.Name = “Patrick Hines”;
contact.Phone =206-555-0144”;
 
contact.Address = new ExpandoObject();
contact.Address.Street =123 Main St”;
contact.Address.City = “Mercer Island”;
contact.Address.State = “WA”;
contact.Address.Postal =68402;

Es di collezione

dynamic contacts = new List<dynamic>();
contacts.Add(new ExpandoObject());
contacts[0].Name = “Patrick Hines”;
contacts[0].Phone =206-555-0144”;
contacts.Add(new ExpandoObject());
contacts[1].Name = “Ellen Adams”;
contacts[1].Phone =206-555-0155”;

Proprietà

Proprietà virtual

Se un metodo o proprietà e definito con l'attributo virtual vuol dire che può essere "overridato" da chi eredita da quella classe. Ossia vuol dire che posso ereditando, da OrderDetail, cambiare l'implementazione di Album e Order:

class MioOrdine
{
    public override Album Album 
    {
        get { /* nuova implementazione */ }
        set { /* nuova implementazione */ }
    }
}

Per default in C# i metodi non sono virtuali quindi la loro implementazione non è modificabile tramite ereditarietà.

Delegati

In C#, un Delegate è uno strumento per ottenere un effetto trigger, qualcosa che chi programma in C o C++ conosce con il nome di callback.
Una caratteristica fondamentale in un'applicazione è quella di registrare una funzione di richiamo, detta appunto callback, con un componente, ottenendo che il componente stesso invochi questa funzione quando avviene qualche evento specifico, come ad esempio, quando si vuole che una certa funzione venga chiamata se un utente seleziona una voce di menu particolare, o quando è verificata una eccezione non gestita, e così via.

Eventi

Vedere anche: Delegati

Es. 1

Esempio da codeproject.
Applicazione Console:
using System;
 
namespace wildert
{
    public class Metronome
    {
        public event TickHandler EventoTicchettio;
        public EventArgs e = null;
        public delegate void TickHandler(Metronome m, EventArgs e);
 
        public void Start()
        {
            while (true)
            {
                System.Threading.Thread.Sleep(3000);
                // E' stato assegnato un Handler per l'evento ?
                if (EventoTicchettio != null) 
                {
                    // Scatena l'evento
                    EventoTicchettio(this, e);
                }
            }
        }
    }
    public class Listener
    {
        public void Subscribe(Metronome m)
        {
            m.EventoTicchettio += new Metronome.TickHandler(HandlerAscoltaIlTicchettio);
        }
        private void HandlerAscoltaIlTicchettio(Metronome m, EventArgs e)
        {
            System.Console.WriteLine("Evento 'sono in ascolto' è stato scatenato");
        }
 
    }
    class Test
    {
        static void Main()
        {
            Metronome m = new Metronome();
            Listener l = new Listener();
            // Quanto segue si esegue solo la prima volta per sottoscrivere l'evento e far puntare al relativo handler
            l.Subscribe(m); 
            m.Start();
        }
    }
}

Es. 2

Il seguente è un esempio reale semplicissimo, si fanno uso risorse di tipo statico. In una prima classe si dichiarano l'evento, il tipo di handler per l'evento previsto ed un oggetto argomento dell'handler da passare al metodo previsto allo scopo.

Nella classe dove creare l'evento da intercettare:

namespace ClassiLibreria
{
    public class UADataSubscriptionHandlers
    {
	// Risorse
        public static event MDERestartHandler MDERestartEvent;
        public static EventArgs e = null;
        public delegate void MDERestartHandler(UADataSubscriptionHandlers s, EventArgs e);
 
	//... etc
 
	// Metodo che genererà l'evento
	public static void GeneraEventoDaIntercettare()
	{
		// Se nella classe client è stato previsto metodo di handling, vedi: UADataSubscriptionHandlers_MDERestartEvent
		if (MDERestartEvent != null) 
		{
			MDERestartEvent(new UADataSubscriptionHandlers(), e); // <-- Ciò scatenerà l'evento !!!
		}		
	}
     }	
}

Nella classe "Client" dove intercettare l'evento generato nella classe precedentemente creata:

using ClassiLibreria;
 
namespace ClassiClient
{
	public class ClasseClient
	{
		public ClasseClient() // Costruttore
		{
			// ... inizializzazioni varie
 
			// Aggancio un metodo ad hoc in questa classe che gestisca l'evento generato:
			UADataSubscriptionHandlers.MDERestartEvent += UADataSubscriptionHandlers_MDERestartEvent;
		}
 
		// etc
 
        	private void UADataSubscriptionHandlers_MDERestartEvent(UADataSubscriptionHandlers s, EventArgs e)
        	{
            		IsStarted = false; // Per uscire da un ciclo WHILE e passare al WatchDog
            		m_logger.Warn(string.Format("IsStarted = false; SecondsTillShutdown {0}", UACore.SrvMDEInfo.SecondsTillShutdown));
        	}		
	}
}

I Generic

Per generics intendiamo una classe, una struttura, un’interfaccia o un metodo nel quale uno o più tipi effettivi vengono sostituiti da parametri al fine da assicurare al codice che si sviluppa una maggiore flessibilità ed indipendenza dai tipi.

I generics quindi consentono lo sviluppo di elementi di programma che svolgono una o più funzioni indipendentemente dai tipi sui quali agiscono e vengono generalmente utilizzati per creare classi di insieme.

Una lista dinamica ad esempio può essere intesa come un contenitore di oggetti generici (ovvero di qualsiasi tipo) ed il comportamento dei metodi di inserimento ed eliminazione di oggetti dalla lista è assolutamente indipendente da quello che è il tipo degli oggetti considerati: in questo caso l’impiego dei tipi generici e dei parametri ci permette di evitare lo sviluppo di una lista per ogni tipo di oggetto che vogliamo trattare ma di crearne una che sia utilizzabile da tutti i tipi.

Naturalmente quando si crea un’istanza di una classe generica occorre specificare i tipi effettivi sui quali questa agisce: tali tipi prenderanno il posto dei parametri di tipo definiti all’interno della classe stessa.

Per parametri di tipo intendiamo i segnaposto utilizzati nella definizione di un tipo o di un metodo al posto dei tipi effettivi per creare un tipo generico.

Classe generica

Esempio:
public class Generica<T> 
{
   public T campo;
   ...
}
in tal modo viene definita una classe generica che utilizza il segnaposto T al posto del tipo effettivo.

Questo significa che nel momento in cui verrà creata un’istanza della classe Generica occorrerà indicare, diversamente da quanto accade tradizionalmente, anche il tipo effettivo corrispondente al segnaposto T.

Se ad esempio si vuole creare un’istanza della classe Generica in cui il tipo del membro della classe campo sia una stringa si dovrà scrivere:

Generica<string> g = new Generica<string>();

se ad esempio si vuole creare un’istanza in cui tipo del membro campo sia un intero si dovrà utilizzare il seguente codice:

Generica<int> g = new Generica<int>();

Una volta definita l’istanza questa può essere trattata come se fosse un’istanza della classe non generica ottenuta sostituendo il parametro di tipo con il tipo effettivo.

Per tornare ai nostri esempi è come se con la scrittura:

Generica<string> g = new Generica<string>();

si creasse un’istanza della classe

public class Generica
{
   public Stringa campo;
   ...
}

Parametri di tipo

Per convenzione nella definizione di un tipo generico si utilizzano dei parametri di tipo il cui nome deve:

  • essere descrittivo, a meno che un nome composto da una singola lettera non pregiudichi la facile comprensione del codice
  • iniziare con la lettera T
public T Somma<T>(T a, T b);

E’ possibile altresì applicare delle restrizioni ai tipi che possono essere utilizzati al momento della creazione di un’istanza di un tipo generico. Es. se si tenta di creare un’istanza di un tipo generico utilizzanto un tipo effettivo non compatibile con quelli che sono i vincoli definiti viene generato un errore in fase di compilazione.

Esistono sei tipi di vincoli tutti applicabili mediante la parola chiave where:

  • "where T: struct" indica che l’argomento di tipo deve essere un tipo valore
  • "where T: class" indica che l’argomento di tipo deve essere un tipo riferimento
  • "where T: new()" indica che l’argomento di tipo deve disporre di un costruttore publico privo di parametro: se utilizzato insieme ad altri vincoli deve essere indicato per ultimo.
  • "where T: nome_classe" indica che l’argomento di tipo deve corrispondere alla classe indicata oppure derivare da essa
  • "where T: nome_interfaccia" indica che l’argomento di tipo deve corrispondere all’interfaccia indicata oppure implementarla
  • "where T: U" indica che l’argomento di tipo fornito per T deve corrispondere a quello fornito per U: si tratta di un vincolo di tipo naked.


E’ bene precisare che su un parametro di tipo è possibile applicare più vincoli separandoli tramite virgola; sui parametri di tipo non vincolati non è possibile utilizzare gli operatori == e != in quanto potrebbero non essere supportati

Classi generiche

Nella progettazione di un’interfaccia generica occorre prestare attenzione a due aspetti:

  • quali sono i tipi da generalizzare in parametri di tipo
  • quali sono i vincoli da applicare ai parametri di tipo


Tipicamente nella creazione di una classe generica si parte da una classe concreta esistente e si modificano di volta in volta i tipi effettivi in parametri di tipo fino a raggiungere un compromesso fra generalizzazione e possibilità di utilizzo.

public class Stack< T >
{
   T[] elements;
   int count;
 
   public void Push(T item) 
   {
      ...
   }
 
   public T Pop() 
   {
      ...
   }
}

Metodi e delegati generici

L’utilizzo dei tipi generici può interessare anche la definizione di metodi e delegati generici: il funzionamento è del tutto analogo a quanto visto per la definizione di classi generiche:

  • si definiscono i parametri di tipo in fase di implementazione del metodo (sia come tipo restituito sia come argomenti del metodo) o del delegato
  • in fase di creazione di instanza si specifica il tipo effettivo da utilizzare
public void Swap<T>(ref T a, ref T b) 
{ 
   T temp; 
   temp = a; 
   a = b; 
   b = temp; 
} 
 
// ..
public delegate void DelegatoGenerico<T>(object oggetto, T datievento);

Extension

E' possibile aggiungere metodi ai tipi fondamentali, senza spiegare molto basta aggiungere this tra i parametri formali di un metodo con un sol parametro, l'estensione sarà applicata al tipo del parametro.

public static class Extensions
{
		public static int GetWeekOfYear(this DateTime date)
		{
			GregorianCalendar cal = new GregorianCalendar(GregorianCalendarTypes.Localized);
			return cal.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
		}
}

Eccezioni

Exception. Come generare l'eccezione per dato nullo, Object not set to an instance of an object.

  if (s == null)
    throw new ArgumentNullException("s");

Mappa e Links


C# | Teoria | Delegati


Soluzioni varie

Parole chiave: Delegate, delegati, delegato, eventi, handler, default, loop, continue, break

Author