Exista o carte, "Concursuri de programare. Probleme si solutii", continand 71 de probleme date la concursurile desfasurate la UPB sau la concursurile la care echipa UPB a luat parte, intre anii 1984-1994. Rezolvarile sunt in Pascal dar ma gandesc ca ar fi interesant sa le transpunem in F#, mai ales ca programarea functionala se preteaza la genul asta de probleme.
Hai sa incepem chiar cu prima problema din culegere, una foarte usoara, data la etapa locala, Bucuresti, 1984 si propusa de Florica Moldoveanu. Se cheama "Echilibrarea balantei":
Se dau m1 greutati de k1 kg fiecare si m2 greutati de k2 kg fiecare. Sa se scrie un program care stabileste modurile in care poate fi echilibrata o balanta avand pe platanul din stanga o greutate x data. Greutatile pot fi puse pe ambele platane. Rezultatele vor aparea sub forma unui tabel cu doua coloane, din care sa rezulte repartizarea greutatilor pe cele doua platane.
Rezolvarea in F# poate arata asa:
let get_balance_solutions m1 k1 m2 k2 x =
seq {
for n1 in [-m1..m1] do
for n2 in [-m2..m2] do
if x = n1 * k1 + n2 * k2 then
let solution =
match sign(n1), sign(n2) with
| 1, -1 -> (0, -n2, n1, 0)
| -1, 1 -> (-n1, 0, 0, n2)
| _, _ when n1 * n2 >= 0 -> (0, 0, n1, n2)
| _, _ -> failwith "invalid data"
yield solution
}
let print_solutions m1 k1 m2 k2 x =
printf "%d weights of %d kg and %d weights of %d kg to balance a given weight of %d kg\n" m1 k1 m2 k2 x
let solutions = get_balance_solutions m1 k1 m2 k2 x
if solutions |> Seq.isEmpty then printf "can't equilibrate the balance\n"
else solutions |> Seq.iter (fun (ln1, ln2, rn1, rn2) ->
printf "%d + %d * %d + %d * %d = %d * %d + %d * %d\n" x ln1 k1 ln2 k2 rn1 k1 rn2 k2)
In prima linie, definim functia get_balance_solutions care va accepta parametrii m1, k1, m2, k2 si x.
Prin seq {...} ne definim o secventa (o secventa genereaza o instanta de tip IEnumerable<T>). Urmatoarele for-uri sunt clare, iar in if vedem cum pentru operatorul de comparatie avem =.
In randurile urmatoare, definim o variabila solution de tip tupla (o tupla este o colectie ordonata de valori grupate impreuna, tratata unitar, fara a fi necesara introducerea unui nou tip)
match-ul care urmeaza reprezinta o constructie foarte puternica de pattern matching, similara cu switch-ul din alte limbaje. Atribuim diverse valori de tuple, in functie de semnele (sign) lui n1 si n2. Mai jos observam si un when, care in F# se cheama when guard si este destinat situatiilor in care dorim adaugarea unor logici custom. Faptul ca avem underscore-uri denota faptul ca nu ne intereseaza valoarea acelui pattern: match-ul se produce daca nici un alt match anterior nu s-a produs si controlul este pasat guard-ului when. La urmatorul pattern matching avem failwith care declanseaza o eroare Microsoft.FSharp.Core.FailureException la runtime, cu mesajul transmis ca parametru - asta pentru ca match-urile anterioare au descris complet situatiile de match posibile si deci am ajunge aici doar in cazul unor date invalide.
yield este similar cu cel din C# si nu face altceva decat sa intoarca un element in secventa
Randurile din print_solutions sunt intuitive, o atentie speciala merita operatorul |> (pipeline spre dreapta) pe care-l vom intalni foarte des si care permite asa numitul stil compozitional de programare. El nu face altceva decat sa rearanjeze ordinea parametrilor unei functii, astfel incat ultimul parametru sa apara primul - definitia lui este foarte simpla:
let (|>) x f = f x
Vom putea rula acum micul program:
print_solutions 5 2 5 1 4
care va tipari pe ecran:
5 weights of 2 kg and 5 weights of 1 kg to balance a given weight of 4 kg
4 + 0 * 2 + 0 * 1 = 0 * 2 + 4 * 1
4 + 0 * 2 + 0 * 1 = 1 * 2 + 2 * 1
4 + 0 * 2 + 0 * 1 = 2 * 2 + 0 * 1
4 + 0 * 2 + 2 * 1 = 3 * 2 + 0 * 1
4 + 0 * 2 + 4 * 1 = 4 * 2 + 0 * 1
sau:
print_solutions 5 2 5 1 11
va tipari pe ecran:
5 weights of 2 kg and 5 weights of 1 kg to balance a given weight of 11 kg
11 + 0 * 2 + 0 * 1 = 3 * 2 + 5 * 1
11 + 0 * 2 + 0 * 1 = 4 * 2 + 3 * 1
11 + 0 * 2 + 0 * 1 = 5 * 2 + 1 * 1
sau, in sfarsit:
print_solutions 5 2 5 1 20
care va tipari pe ecran:
5 weights of 2 kg and 5 weights of 1 kg to balance a given weight of 20 kg
can't equilibrate the balance
Una din problemele cele mai dificile la proiectarea unor template-uri pentru generarea de cod este separarea completa a codului generat de cel custom si gasirea punctelor de extensie cele mai potrivite. In octombrie anul trecut, dintr-o discutie cu colegul meu Daniel Severin, a rezultat o situatie care mi s-a parut interesanta: sa presupunem ca un tool va genereaza o clasa partiala care implementeaza IDisposable:
using System;
partial class Foo : IDisposable {
public void Dispose() {
Console.WriteLine("implicit");
}
}
In codul vostru utilizati clasa Foo, evident intr-un using:
using(Foo foo = new Foo()){}
Cum puteti schimba/customiza comportamentul din Dispose fara a modifica codul generat? Simplu: implementand explicit interfata IDisposable:
using System;
partial class Foo {
void IDisposable.Dispose() {
Console.WriteLine("explicit");
}
}
In acest fel, using(Foo foo = new Foo()){} va tipari cuvantul explicit pe ecran si nu cuvantul implicit.
In comentariul pe care l-a lasat Raffaele Rialdi la postul meu, spunea ca tocmai in acea perioada utiliza implementarea explicita de interfete intr-un proiect legacy scris un pic haotic in care nu era foarte clar ce metode/proprietati erau apelate . Rezultatul? Crearea de interfete si implementarea lor explicita pentru a permite un refactoring total al claselor, realizand in acest mod o evolutie mai lina.
In postul de ieri, scoteam in evidenta faptul ca intr-un anumit caz, comportamentul compilatorului C# este diferit de al compilatoarelor pentru Visual Basic .NET, C++/CLI si Visual J#. Si cum in perioada asta am avut un pic timp sa ma joc mai mult cu F#, m-am gandit sa vedem care este comportamentul compilatorului de F#.
Codul C# in cauza, era:
using System;
class Foo {
public virtual void Write(string s) {
Console.WriteLine("Foo virtual " + s);
}
}
class Bar : Foo {
public override void Write(string s) {
Console.WriteLine("Bar override " + s);
}
public void Write(string s, params string[] args) {
Write("Bar overload " + s);
}
}
class Program {
static void Main() {
Bar bar = new Bar();
bar.Write("Ciao!"); // Process is terminated due to StackOverflowException
}
}
Voi scrie mai jos codul echivalent in F#, dupa care il vom folosi ca pretext pentru o scurta intrducere in acest nou limbaj:
open System
type Foo() =
abstract Write : string -> unit
default x.Write(s : string) =
printf "%s" s
type Bar() =
inherit Foo()
override x.Write(s) =
printf "Bar override %s" s
member x.Write(s: string, [<ParamArray>] args: string[]) =
printf "Bar overload %s" s
[<EntryPoint>]
let main _ =
let bar = new Bar()
bar.Write("Ciao!")
0
Primul rand e simplu: open System nu e decat o declaratie de import care in F# se face cu directiva open
Despre urmatorul rand, type Foo() = deja putem spune mai multe lucruri. Prin type ne putem defini un tip, compilatorul in cazul codului nostru va deduce (!) ca vrem sa definim o clasa, am fi putut declara si explicit acest lucru, iar prin faptul ca numele tipului e urmat de o pereche de paranteze definim un constructor de default cu implementare "goala". Constructorul definit in acest fel (putea sa aiba si parametri), e o constructie speciala a F# si se numeste primary constructor.
Avem apoi abstract Write : string -> unit care declara o metoda Write care accepta un string si nu intoarce nimic. string -> unit este semnatura metodei. unit este un tip special, pentru care valoarea null reprezinta singura valoare posibila a acestui tip - este similar void-ului. Acest abstract impreuna cu default-ul de pe randul urmator, default x.Write(s : string) =, sunt echivalentul lui virtual. Ce reprezinta acel "x"? In F# se cheama self identifier si puteti pune orice identificator valid in locul lui x, de exemplu this, self, sau chiar _. Self identifier-ul foloseste la referirea instantei clasei in corpul metodei, neexistand un this ca in C#.
printf "%s" s este clar ce face - specificatorul de format "%s" e pentru string-uri.
Tipul Bar e clar ca deriva din Foo si este si el dotat prin parantezele care insotesc numele tipului de un primary constructor de default. Avem paranteze si dupa tipul de baza in inherit pentru ca ele definesc modul de apelare a constructorului tipului de baza din constructorul tipului derivat.
Celelalte randuri din tipul Bar sunt clare, de exemplu in x.Write(s) puteam sa las x.Write(s: string) dar nu ar fi adaugat nici o informatie in plus, pentru ca tipul parametrului compilatorul il deduce (type inference). [<ParamArray>]> este un atribut cu care am decorat un parametru pentru a declara o lista variabila de parametri - la decorarea cu un atribut, numele atributului (fara sufixul Attribute) este incadrat de "[<" si ">]".
Cu [<EntryPoint>] am decorat metoda main (puteam sa-i spunem oricum altfel), pe care o definim cu let, in pur stil functional. Underscore-ul dupa main e chiar numele parametrului lui main - si aici puteam sa-i dam alt nume, in limita identificatorilor valizi.
0-ul de la sfarsit este valoarea (de exit code) intoarsa de main - suntem obligati de semnatura entry point-ului. Puteam sa renuntam la decorarea cu EntryPoint, caz in care am fi avut:
let main() =
let bar = new Bar()
bar.Write("Ciao!")
main()
Anul trecut undeva cam pe vremea asta eram in Italia pentru vreo saptamana la firma mama si seara tarziu cand ma retrageam in cochetul meu hotel, undeva in mijlocul campului, pe langa Modena, nu mai aveam vlaga decat sa-mi citesc mailul. Asa se face ca mi-a scapat un post in care Diego Martelli semnala un comportament "ciudat" in C#. Un bun amic, Nicolò Carandini, imi scrie insa imediat un email, cerandu-mi parerea asupra comportamentului semnalat de Diego. Chestiunea mi s-a parut interesanta si in jurul ei am scris doua posturi, pe care vreau sa vi le prezint impreuna astazi.
In primul post, propuneam un quiz, pornind de la observatia lui Diego - multi dintre voi v-ati astepta probabil ca urmatoarea bucata de cod:
using System;
class Foo {
public virtual void Write(string s) {
Console.WriteLine("Foo virtual " + s);
}
}
class Bar : Foo {
public override void Write(string s) {
Console.WriteLine("Bar override " + s);
}
public void Write(string s, params string[] args) {
Write("Bar overload " + s);
}
}
class Program {
static void Main() {
Bar bar = new Bar();
bar.Write("Ciao!");
}
}
sa tipareasca "Bar override Ciao!" si totusi programul intra in stack overflow! Cum asa? Pentru ca metoda apelata este Write cu lista variabila de parametri si nu cea in override. Acest comportament este datorat faptului ca am instantiat bar ca:
Bar bar = new Bar();
si nu ca:
Foo bar = new Bar();
Metoda Write in override in clasa Bar nu face altceva decat sa specializeze metoda virtuala a clasei de baza Foo, si deci pierde in fata metodelor in overload ale clasei Bar pentru ca Write in forma sa extinsa (vezi paragraful 14.4.2.1 din specificatia ECMA-334) are un parametru de tip string si zero elemente in array-ul de parametri.
Plecand de la considerentele de mai sus, ce credeti ca tipareste urmatorul program?
using System;
class Foo {
public override string ToString() {
return "override ";
}
public string ToString(params string[] args) {
return "overload ";
}
}
class Program {
static void Main() {
Foo foo = new Foo();
Console.Write(foo);
Console.WriteLine(foo.ToString());
}
}
- A: override override
- B: override overload
- C: overload overload
Raspunsul corect este B si a fost dat de Eber Irigoyen si de prietenul meu Roberto Albano.
O zi mai tarziu, incercand sa vad cum se comporta in aceasta situatie alte limbaje .NET, cum ar fi Visual Basic .NET, C++/CLI si Visual J#, nu mica mi-a fost mirarea sa descopar ca C# e cel care face exceptie, comportandu-se diferit fata de celelalte limbaje, motiv pentru care am scris imediat un alt post, "Quando C# e' piuttosto l'eccezione che la regola" (Cand C# e mai degraba exceptia decat regula), in care arat ca doar codul C# intra in stack overflow:
using System;
class Foo {
public virtual void Write(string s) {
Console.WriteLine("Foo virtual " + s);
}
}
class Bar : Foo {
public override void Write(string s) {
Console.WriteLine("Bar override " + s);
}
public void Write(string s, params string[] args) {
Write("Bar overload " + s);
}
}
class Program {
static void Main() {
Bar bar = new Bar();
bar.Write("Ciao!"); // Process is terminated due to StackOverflowException
}
}
in timp ce pentru celelalte limbaje enumerate mai sus, tipareste Bar override Ciao!:
' Visual Basic .NET
Imports System
Class Foo
Public Overridable Sub Write(ByVal s As String)
Console.WriteLine("Foo virtual " + s)
End Sub
End Class
Class Bar : Inherits Foo
Public Overrides Sub Write(ByVal s As String)
Console.WriteLine("Bar override " + s)
End Sub
Public Overloads Sub Write(ByVal s As String, ParamArray args As String())
Write("Bar overload " + s)
End Sub
End Class
Module Program
Sub Main
Dim bar As Bar = New Bar
bar.Write("Ciao") ' tipareste Bar override Ciao!
End Sub
End Module
// C++/CLI
using namespace System;
ref class Foo {
public:
virtual void Write(String^ s) {
Console::WriteLine("Foo virtual " + s);
}
};
ref class Bar : Foo {
public:
virtual void Write(String^ s) override {
Console::WriteLine("Bar override " + s);
}
void Write(String^ s, ... array<String^>^ args) {
Write("Bar overload " + s);
}
};
int main() {
Bar^ bar = gcnew Bar;
bar->Write("Ciao!"); // tipareste Bar override Ciao!
};
// Visual J#
import System.*;
class Foo {
public void Write(String s) {
Console.WriteLine("Foo virtual " + s);
}
}
class Bar extends Foo {
public void Write(String s) {
Console.WriteLine("Bar override " + s);
}
public void Write(String s, /** @attribute ParamArray() */ String[] args) {
Write("Bar overload " + s);
}
}
class Program {
public static void main(String[] args) {
Bar bar = new Bar();
bar.Write("Ciao"); // tipareste Bar override Ciao!
}
}
C# e considerat de multi un limbaj "central" al .NET-ului si in acest sens mi s-a parut ciudat ca tocmai comportamentul lui sa sa distanteze de comportamentul celorlalte limbaje .NET in acest caz specific. Voua care comportament vi se pare mai intuitiv? Voi mai face in viitor paralele intre diferite limbaje .NET si in alte posturi.
Post-ul pe care-l prezint astazi l-am publicat in ianuarie, "Sul cast del foreach" si incearca sa limpezeasca o nedumerire lansata de amicul meu Luca Minudel in post-ul sau "foreach l'insidioso" (foreach inselatorul ar veni in traducere). Daca cineva dintre voi a simtit nevoia tipurilor nullable si inainte de 2.0, cu singuranta a nimerit peste proiectul lui Luca, denumit - cum altfel? - Nullable Types, inceput prin 2003 daca nu ma inseala memoria. El a fost si cel care a organizat partea de wiki a Ugidotnet, parte care luase destula amploare. De la inceputul anului si-a schimbat jobul si s-a mutat in Suedia, iar inainte a lucrat ca senior software engineer pentru Ferrari F1. Blogheaza frecvent (705 post-uri pana acum) si de ceva vreme in principal pe problematicile agile, Scrum, XP, lean, etc.
Revenind la post, Luca se intreaba de ce urmatorul cod compileaza:
using System.Collections.Generic;
interface IPersistent { }
class Invoice : IPersistent { }
class Order : IPersistent { }
class Program {
static void Main() {
List<IPersistent> changedDocuments = new List<IPersistent>();
changedDocuments.Add(new Invoice());
changedDocuments.Add(new Order());
foreach (Invoice changedInvoice in changedDocuments) { }
}
}
Se poate observa si din codul IL cum enumeratorul din foreach face un downcast la Invoice:
call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class IPersistent>::get_Current()
castclass Invoice
Lui Luca, comportamentul acesta i se parea contraintuitiv si riscant pentru ca poate "ascunde" exceptia System.InvalidCastException care va apare abia la runtime daca se executa codul de mai sus si se intreba asupra avantajelor pe care aceasta nesemnalare a unei erori la compile-time i le-ar aduce. Luca s-ar fi asteptat ca foreach-ul sa accepte ca tip al elementului doar IPersistent sau un tip de baza al lui IPersistent, in caz contrar sa obtinem eroare la compilare.
In cazuri de genul acesta, raspunsul se gaseste in specificatia limbajului C#, ECMA-334, neintrecuta de nici o alta carte de C# la nivel de profunzime si precizie a exprimarii. Veti vedea in multe din post-urile care vor urma cat de des apelez la ea si cum de fiecare data gasim raspunsul cel mai potrivit cu un pic de rabdare si atentie. Iata de exemplu ce spune paragraful 15.8.4 din ECMA-334:
"A foreach statement of the form
foreach(V v in x) embedded-statement
is then expanded to:
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
The variable e is not visible to or accessible to the expression x or the embedded statement or any other source code of the program. The variable v is read-only in the embedded statement. If there is not an explicit conversion from T (the element type) to V (the type in the foreach statement), an error is produced and no further steps are taken."
Specificatia spune clar ca daca nu exista o conversie explicita de la tipul real al elementului la tipul din foreach, avem de a face cu o exceptie la runtime si nici un alt pas al foreach-ului nu se mai executa. Ce inseamna asta? Inseamna ca e responsabilitatea programatorului aceea de a se asigura ca acel cast este valid. Am putea sa vrem de exemplu sa facem un foreach pe toate Label-urile dintr-o ControlCollection a controalelor dintr-o Form - va fi sarcina noastra aceea de a "filtra" elementele de tipul Label si sa facem foreach-ul doar pe acestea sau sa ne asiguram ca in Controls sunt doar Label. De ce nu ne ajuta compilatorul? Probabil pentru ca acea lista ar putea fi construita in alta parte!
Oricum, daca cautam un ajutor din partea intellisense-ului si al compilatorului, putem sa inlocuim foreach lui Luca, cu:
changedDocuments.ForEach(delegate(IPersistent persistent) { });
Surprinzator, obtinem chiar un cod IL mai mic decat in varianta foreach-ului (aceasta neinsemnand totusi neaparat o performanta superioara). Type parametrul argumentului action a metodei ForEach, e constrans acum sa fie identic cu type parametrul List-ei noastre, adica cu IPersistent.
In comentariul pe care Luca l-a lasat la post-ul meu, considera explicatia data ca fiind detaliata si interesanta, explicatie care-i lamureste orice dubiu asupra chestiunii, iar alternativa metodei ForEach() recunoaste ca ii ofera un mod de a obtine verificarea la compile-time asa cum dorea. N-a ramas insa convins de avantajele intr-un caz real al comportamentului descris de specificatii pentru ca, spune el, daca ar vrea sa filtreze label-urile controalelor unei Form ar face oricum o metoda care sa-i intoarca o colectie de Label. E adevarat, dar, zic eu, constrangerea asupra tipului intors al metodei ar putea veni dintr-un tip de baza, clasa sau interfata, de exemplu.
Blog revival: Acum aproape un an si jumatate am incercat pe acest blog sa apropii de voi cumva comunitatile .NET din Italia, traducand unele post-uri care mi s-au parut mai interesante. N-a prins cine stie ce, probabil pentru ca subiectele le-am ales prea eterogene, incercand mai mult sa vad ce va place, ce v-ar putea starni interesul pe mai incolo, dar si din vina mea ca am abandonat prea usor totul. La Community Bootcamp-ul de anul acesta am fost incurajat sa reiau blog-ul, de data asta in mod sustinut, de exemplu incepand sa traduc sutele de post-uri (440 pana acum) pe care le-am scris din martie 2004 pe blogul meu in italiana. Andrei imi sugera sa incep cu lunga mea serie de quiz-uri de C#, dar cred ca o sa le las pe acestea la urma si o sa incep in ordine invers cronologica cu post-urile tehnice legate in principal de C# si de chestiuni de arhitectura. Voi incerca sa nu ma rezum doar la o traducere chioara ci sa incerc sa dezvolt respectivele post-uri, acolo unde consider (sau considerati!) ca e necesar - feedback-ul vostru constructiv, va fi intotdeauna foarte apreciat. Din experienta mea, valorea pe care un blog o poate aduce sta jumatate in calitatea post-urilor si jumatate in feedback-ul pe care reuseste sa-l starneasca.
Post-ul cu care incep l-am scris in aprilie si se cheama "MVC versus Document/View in MFC". Cam pe atunci mi-a venit colet una din cele doua carti pe care le-am ales pentru kit-ul meu de Community Influencer: "Microsoft .NET: Architecting Applications for the Enterprise". Cartea asta imi e nemaipomenit de draga pentru ca strange laolalta ideile de arhitectura pe care bunul meu amic Andrea Saltarello le-a expus la diferite workshop-uri (vreo 3 ani la rand cred ca am participat la toate, uneori ca si speaker), care se terminau in nesfarsite cine la restaurant cu discutii de neuitat pe aceleasi teme si in care am simtit adevaratul spirit de comunitate. Citind-o, la aproape fiecare capitol si subcapitol, reuseam sa-mi aduc aminte ocaziile in care acele idei s-au nascut. Cu timpul, am vazut cum Dino intra din ce in ce mai mult in joc, apropiindu-se de arhitectura, si pana la urma iata, cu incredibilul sau talent pentru a scrie carti, a reusit sa stranga laolalta ideile lui Andrea intr-o minunata carte. V-o recomand: e pacat sa nu cititi cartea lui Fowler sau cea a lui Nilsson, fara sa le completati cu cartea lui Dino si Andrea. Veti vedea cum pattern-urile lui Fowler, un pic prea abstract prezentate de Fowler in opinia mea, incep sa prinda contur si sens in cartea de care va spun.
Si acum sa trec la subiectul postului de azi: deci incepusem sa citesc cartea si la pagina 362, la introducerea subcapitolului "Model2: A Web Variation of MVC", dupa ce se prezinta faptul ca pana la aparitia lui ASP.NET MVC, pattern-ul MVC n-a fost prea popular pe platforma Windows, se aduce in discutie un pattern cu care MVC-ul are cateva puncte in comun (view-ul si controller-ul sunt "topite" impreuna), si anume Document/View, implementat in MFC. Andrea si Dino se intreaba apoi asupra motivelor pentru care echipa MFC a renuntat la varianta clasica a MVC-ul pentru a crea pattern-ul Document/View:
We really don't know whether the MFC team intentionally discarded MVC to embrace DV; our feeling, however, is that DV blossomed autonomously during the design by simply applying correctly and diffusely the SoC principle
Ce motiv sa fi avut cei din echipa MFC sa nu adopte MVC si sa creeze un alt pattern? Iata istoria: se pare ca la PDC-ul din 1995, cineva din echipa MFC a povestit chiar pe s-a bazat aceasta decizie, MFC versus DV, atunci cand au facut alegerea arhitecturala pentru MFC:
[...] for the controller’s message mapping mechanism, can’t we just reuse MFC’s message map solution? Ideally, yes. But there is one big problem here... A separate controller class for message handlers may sound like a good idea, but MFC was designed in such a way that this was thought impossible. MFC allows commands such as menu picks to be rerouted to non-windows, but not windows messages such as mouse clicks. In MFC, only a CWnd can receive windows messages and only one instance of a CWnd can receive the messages for a real Windows window. Since a controller is meant to be a non-window that can handle windows messages and exist in multiple instances per window, we seem to be stuck! An interesting aside, this problem was sited by an original MFC team member as the main reason Microsoft emulated MVC for the CDocument and CView, but stopped short of implementing a controller
Interesant, nu? Clasa care se ocupa de date este document-ul (termenul trebuie luat in sens absolut general, nu se refera la "documente" in sens strict ci in general la date), iar codul care renderizeaza datele si care e responsabil cu interactiunea cu utilizatorul este view-ul. Practic datorita CWnd-ului in sarcina caruia stau toate functionalitatile unei ferestre, inclusiv cele de receptie a mesajelor, controller-ul s-a contopit cu view-ul, dand nastere la Document/View. Aceasta arhitectura Document/View n-a fost creata de la inceput, odata cu MFC-ul, ci a aparut abia in versiunea 2.0 a MFC, in 1993. Prea stransa interdependenta (Blaszczak o numeste "consciousness" - vezi cartea lui la p. 163) intre document si view poate crea probleme la reutilizarea claselor (vezi tot Blaszczak, p. 214), motiv pentru care altii s-au gandit la o alternativa, care sa implementeze totusi MVC-ul clasic nerenuntand la MFC (practic extensii ale MFC): Objective Toolkit care vine cu Rogue Wave Stingray Studio
Pentru aprofundarea pattern-ului Document/View puteti incepe cu partea a 4-a a cartii Introduction to MFC Programming with Visual C++, pp. 259-290 (mi s-a parut la vremea cand programam in Visual C++ de departe cea mai simpla introducere in MFC) sau direct cu "biblia" MFC-ului, care este cartea lui Mike Blaszczak (a fost o vreme chiar Program Manager pentru MFC, din care cauza cartea sa e cat se poate de "inside"), Professional MFC With Visual C++ 6 in care gasiti amplul capitol 4 (pp. 155-237) destinat exclusiv acestui subiect. Pentru a vedea cum Objective Toolkit reuseste totusi sa implementeze MVC extinzand MFC-ul, cititi pagina de la acest link.
Emanuele DelBono e unul din fondatorii UGIALT.net, o persoana extrem de prietenoasa si entuziasta. Il putetzi vedea in aceasta excelenta fotografie realizata de Carolyn Jones (e cel cu semaforul in bratze) din cartea Heroes Happen {here} comandata de Microsoft pentru binecunoscutul eveniment. Post-ul original dupa care am facut traducerea e la adresa: http://blogs.ugidotnet.org/BlogEma/archive/2008/06/17/windsor-castle-factory-support.aspx.
Multi dintre noi cunosc Windsor Castle poate cel mai faimos container IoC. Putzini insa stiu ca in afara de a putea fi utilizat ca simplu container, Castle dispune de cateva "facility" care permit extinderea functionarii sale. In cele ce urmeaza as vrea sa vorbesc despre Factory.
Implicit, un obiect inregistrat intr-un fisier de configurare Castle este construit utilizand constructorul sau caruia eventual i se
injecteaza parametrii necesari. Uneori insa e posibil sa trebuiasca sa instantziem obiectul folosindu-ne de o clasa factory care se ocupa de construirea si setup-ul acestuia. Castle suporta in mod nativ acest mod de instantziere a obiectelor, e suficient sa fie configurat in modul corect.
Sa presupunem ca vrem sa configuram Castle pentru a obtzine instantza unei sesiuni NHibernate - dupa cum stitzi sesiunea nu poate fi construita cu new ci trebuie construita cu ajutorul unei clase SessionFactory de obicei incapsulata intr-un NHibernateHelper:
public static class NHibernateHelper
{
private static readonly ISessionFactory sessionFactory;
static NHibernateHelper() {
Configuration cfg = new Configuration();
cfg.Configure();
sessionFactory = cfg.BuildSessionFactory();
}
public static ISession GetNewSession() {
return sessionFactory.OpenSession();
}
// ...
}
Clasa NHibernateHelper are o metoda statica GetNewSession care ntoarce o instanta de ISession. Vreau sa utilizez aceasta clasa cu ajutorul IoC pentru construirea unor ISession de injectat de exemplu intr-un Repository:
public class UserRepository : IUserRepository {
public UserRepository(ISession session) {
//...
}
}
Singura chestie de facut e sa instruim Castle-ul prin fisierul de configurare (sau prin cod, dupa cum preferati):
<castle>
<facilities>
<facility id="factorysupport" type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.Microkernel" />
</facilities>
<components>
<component id="session"
type="NHibernate.ISession, NHibernate"
factoryId="sessionFactory"
factoryCreate="GetNewSession" />
<component id="sessionFactory"
type="NHibernateHelper, CodicePlastico.Samples" />
<component id="userRepository"
service="CodicePlastico.Samples.Repositories.IUserRepository, CodicePlastico.Samples"
type="CodicePlastico.Samples.UserRepository, CodicePlastico.Samples" />
</components>
</castle>
Primul element facilities adauga suportul necesar pentru factory specificand care clasa se va ocupa de gestiunea acestor factory in Castle. Dupa care sunt inregistrate componentele. Prima componenta (session) este obiectul care va fi creat printr-o factory, pentru care specific tipul (ISession) si id-ul componentei care va fi factory pentru ISession (sessionFactory) si metoda care va trebui sa fie utilizata pentru constructia obiectelor ISession (GetNewSession). Evident si clasa factory trebuie inregistrata (componenta sessionFactory) cu aceeasi sintaxa. La fel si pentru clasa UserRepository.
Ce se intampla cand cer containerului un obiect de tip IUserRepository? Containerul vede ca tipul concret este UserRepository si ca constructorul sau primeste ca parametru o instantza de ISession care la randul ei trebuie sa fie construita cu ajutorul metodei GetNewSession din clasa NHibernateHelper.
Aceasta tehnica e utila in acele situatzii in care obiectul nu poate fi creat direct ci trebuie utilizata o alta clasa care sa se ocupe de instantziere.
Cine n-a auzit de Dino Esposito? "Showing 1 - 12 of 82 Results" la o simpla cautare a lui pe Amazon. In afara de a fi un adevarat guru e si o persoana plina de culoare. Varianta originala a post-ului tradus mai jos o gasiti aici: http://blogs.ugidotnet.org/dinoes/archive/2008/04/09/92146.aspx.
Intotdeauna m-am intrebat la ce-ar putea sa foloseasca proprietatea BehaviourID in controalele din AJAX Control Toolkit. Era cat pe ce sa gafez la cursuri atunci cand imi aparea pe neasteptate in IntelliSense. E un string care identifica in mod unic o componenta. Care-i diferentza fatza de ID? Proprietatea BehaviourID se foloseste pentru a recupera via script instantza componentei:
var extender = $find("...");
Daca BehaviourID nu e specificata trebuie sa pasam $find-ului, ID-ul controlului. Care, in cazul unei master page e un string dificil de retzinut. Atunci trebuie sa se faca apel la urmatorul cod:
var extender = $find("<%= Extender1.UniqueID %>");
Pentru a evita asta, e de preferat sa fie setat BehaviourID a carui valoare nu e influentzata de prezentza unei master page.
E usor sa-l recunosti pe Marco Bellinaso intr-o multzime de cateva sute de persoane, cate se strang de regula la un intalnire .NET: e cel cu pletele mai lungi decat parul oricarei tipe din sala, ultima oara cand l-am intalnit le avea pana la centura :) Lucreaza in aceasi firma cu cei doi MS Regional Director pe Italia, Francesco Balena si Giuseppe di Mauro iar cartea sa de ASP.NET 1.0 aparuta in 2002 cred ca poate fi considerata ca prima carte buna aparuta pe ASP.NET. Post-ul pe care l-am ales e aparut acum vreo luna: http://www.marcobellinaso.com/blog-ita/post/Importare-dati-da-Excel-a-SQL-Server-2005-Express.aspx.
Cand, cu cateva zile in urma mi s-a cerut sa import datele dintr-o foaie Excel intr-un tabel in SQL Server 2005, mi-am zis "no problem, lansez wizard-ul Data Import/Export si gata". Din pacate acest tool nu se gaseste in versiunea Express din moment ce in aceasta versiune lipsesc si serviciile SQL Server Integration Services (SSIS) si SQL Server Agent, pe care se bazeaza wizard-ul. N-am vrut sa instalez versiunea Standard doar pentru asta asa ca am cautat o solutzie alternativa. E posibil de exemplu sa scriu un macro VBA direct in Excel care sa cicleze pe randuri si coloane si sa inserez datele in SQL Server cu ajutorul vechiului Recordset din ADO... dar mi s-a parut prea mult de munca pentru o chestie atat de simpla! Solutzia pe care am adoptat-o (si care a functionat perfect) a fost aceea de a utiliza functia OPENROWSET in felul urmator:
SELECT * INTO Magazine FROM
OPENROWSET('Microsoft.Jet.OLEDB.4.0',
'Excel 8.0;Database=c:\temp\DateMagazine.xls', [Sheet1$])
Codul asta ia toate datele existente in fisierul XLS specificat (foaia Excel) si le copie intr-o noua tabela numita Magazine. Primul rand din foaia Excel ar trebui sa fie de antet si pe care comanda de mai sus ar trebui s-o foloseasca pentru a extrage numele campurilor de creat in noua tabela. Daca anumite campuri s-ar crea in acest fel cu un alt tip fatza de cel dorit (de exemplu float in loc de int, sau varchar in loc de nvarchar) este evident posibil sa se modifice schema tabelei direct din Management Studio Express dupa import.
Comanda totusi nu va functiona daca interogarile "Ad Hoc Distributed Queries" nu sunt abilitate. Pentru a le activa, executatzi urmatoarele instructiuni:
sp_configure 'show advanced options',1
reconfigure with override
go
sp_configure 'Ad Hoc Distributed Queries',1
reconfigure with override
go
Alessandro Ghizzardi e un personaj cu poanta mereu pe varful limbii, un "dracusor" simpatic cu care se rade intr-una dar care munceste la greu. Este MVP ASP.NET si impreuna cu o mana de altzi amici a fondat in 2002 UGIdotNET care avea sa devina destul de repede cea mai mare comunitate .NET din Italia si din Europa. Pe vremea cand blog-urile pe UGIdotNET aveau ca motor o chestie facuta de ei, fiecare blog avea in adresa doar un ID numeric, incremental (fara nume) iar ID-ul lui Alessandro era chiar 1 - al meu de exemplu era 7322 :-) Are o gramada de post-uri foarte utile in categoria Daily Issue in care scrie exact despre problema cu care s-a confruntat in ziua respectiva. Din aceasta categorie face parte si post-ul pe care vi-l propun acum in traducere: http://blogs.ugidotnet.org/Box/archive/2007/11/15/serializzazione-di-una-classe-con-attributi-xmlelementattribute.aspx.
Am vorbit cu ceva timp in urma despre cum sa gestionezi serializarea unei clase cu XmlSerializer utilizand XmlElementAttribute pentru a avea denumirea elementelor XML diferita de cea a campurilor clasei. Zilele astea s-a intamplat sa trebuiasca sa adaug atribute la nodurile XML-ului meu si in acest caz lucrurile se complica un pic...
Concret, clasa de utilizat este XmlAttributeAttribute, care e un atribut cu care se pot decora elementele unei clase in acelasi fel cu XmlElementAttribute explicat in post-ul referit mai sus.
Problema e ca XmlAttribute adauga atributul la nodul curent. Deci nu putem sa-l punem in ierarhie ca si cand ar fi un element normal ci trebuie sa adaugam un obiect nested in cazul in care ne trebuie un atribut.
Sa ma explic mai bine: daca as fi vrut sa-mi rezulte un XML de genul:
<PERSOANE>
<PERSOANA>
<PRENUME>Alessandro</PRENUME>
<NUME>Ghizzardi</NUME>
</PERSOANA>
</PERSOANE>
as fi creat aceste clase:
[Serializable]
[XmlElement("PERSOANE")]
public class Persoane
{
[XmlElement("PERSOANA")]
public PersoaneCollection Persoane;
}
[Serializable]
public class PersoaneCollection : List<Persoana>
{
}
[Serializable]
public class Persoana
{
[XmlElement("PRENUME")]
public string Prenume;
[XmlElement("NUME")]
public string Nume;
}
Sa presupunem in schimb ca XML-ul ar trebui sa aiba niste atribute. Daca vreau sa adaug niste atribute la vreun nod care nu are contzinut nu prea am probleme. Dar zicem c-as vrea ca XML-ul sa devina:
<PERSOANE>
<PERSOANA Id="1">
<PRENUME>Alessandro</PRENUME>
<NUME>Ghizzardi</NUME>
</PERSOANA>
</PERSOANE>
E suficient sa adaugam un element la clasa Persoana pe care sa-l decoram cu atributul corect:
[Serializable]
public class Persoana
{
[XmlAttribute("Id")]
public int Id;
[XmlElement("PRENUME")]
public string Prenume;
[XmlElement("NUME")]
public string Nume;
}
pentru ca nodul PERSOANA nu contzine text ci doar elemente copii. Daca in schimb vreau sa adaug un atribut la un element care contzine text, se complica un pic chestia... Sa zicem ca as vrea sa obtzin XML-ul urmator:
<PERSOANE>
<PERSOANA>
<PRENUME DeControlat="True">Alessandro</PRENUME>
<NUME>Ghizzardi</NUME>
</PERSOANA>
</PERSOANE>
Am probleme deoarece PRENUME se mapeaza la un obiect String, care String nu poate avea mai mult de un atribut asociat. Si daca lipesc un XmlAttribute la string, acesta va fi asociat nodului PERSOANA si nu nodului PRENUME la carre n-as sti apoi cum sa asociez textul...
Deci, trebuie sa modific clasele mele adaugand un obiect care sa se ocupe de renderizarea nodului PRENUME. Si aici imi vine in ajutor atributul XmlTextAttribute care "instruieste" obiectul sa nu utilizeze un string ca nod ci ca text. Deci exemplul nostru s-ar traduce in:
[Serializable]
[XmlElement("PERSOANE")]
public class Persoane
{
[XmlElement("PERSOANA")]
public PersoaneCollection Persoane;
}
[Serializable]
public class PersoaneCollection : List<Persoana>
{
}
[Serializable]
public class Persoana
{
[XmlElement("PRENUME")]
public PrenumePersoana Prenume;
[XmlElement("NUME")]
public string Nume;
}
[Serializable]
public class PrenumePersoana
{
[XmlAttribute("DeControlat")
public bool DeControlat;
[XmlText()]
public string Text;
}
Observatzi ca Persoana.Prenume nu mai e un string ci un obiect de tip PrenumePersoana care contzine atributul si textul, amandoi decoratzi cu atributele specifice.
De ce am ajuns sa ma lovesc de chestiile astea? Din cauza ca trebuie sa interactzionez cu Flash interactziune care, in destule cazuri se dovedeste nu prea comoda. Nu din cauza tehnologiei in sine ci din cauza celui care o utilizeaza :)
Mauro Servienti e un MVP C# mereu cu zambetul pe buze. A scris vreo 10 articole excelente pentru MSDN Italia, care ar merita traduse. In acest post, propune o solutie simpla dar simpatica pentru a obtzine independentza fatza de un motor IoC: http://blogs.ugidotnet.org/topics/archive/2008/05/25/iservicecontainer.aspx
Intr-un alt post de acum cateva zile mi s-a cerut sa aprofundez cateva concepte.
Incepem cu primul: IServiceContainer.
Cum poate ati intzeles deja sunt un pic obsedat de problema dependentzelor si cum in foarte multe cazuri costa foarte putzin sa te eliberezi de aceasta problema, merita rezolvata. Concret, in aceasta perioada aici in echipa, facem uz masiv de Castle Windsor ca motor de Inversion of Control dar obsesia mea imi zice ca nu e frumos sa depinzi de Castle pentru ca maine poate am vrea sa utilizam Spring.NET sau Unity... deci?
Foarte simplu, abstragem folosirea lui Castle, ascunzand-o. In acest moment ne folosim doar de functionalitatile de IoC deci avem o interfatza simpla:
public interface IServiceContainer
{
T GetService<T>() where T : IService;
IService GetService(Type serviceType);
void Release(object instance);
}
Aplicatzia la randul ei, la runtime, tot prin IoC (dar cu o implementare custom, evident nu cu Castle), obtzine o instantza a unei clase concrete "CastleServiceContainer" care implementeaza interfatza in discutzie:
public sealed class ServiceContainer : IServiceContainer
{
private static IServiceContainer _instance = new ServiceContainer();
public static IServiceContainer GetContainer()
{
return _instance;
}
private ServiceContainer()
{
Type t = Type.GetType(Settings.Default.ServiceProviderType, true);
this.provider = Activator.CreateInstance(t) as IServiceContainer;
}
private IServiceContainer provider;
public T GetService<T>() where T : IService
{
return this.provider.GetService<T>();
}
public IService GetService(Type serviceType)
{
return this.provider.GetService(serviceType);
}
public void Release(object instance)
{
this.provider.Release(instance);
}
}
Si in final avem "provider"-ul propriu zis, in cazul asta cel care utilizeaza Castle Windsor (si e unicul care stie asta).
public class CastleServiceProvider : IServiceContainer
{
IWindsorContainer container = null;
public CastleServiceProvider()
{
this.container = new WindsorContainer(
new XmlInterpreter(
new ConfigResource("applicationSettings/ServiceProvider.Configuration")
)
);
}
public T GetService<T>() where T : IService
{
return this.container.GetService<T>();
}
public IService GetService(Type serviceType)
{
object obj = this.container.GetService(serviceType);
return (IService)obj;
}
public void Release(object instance)
{
this.container.Release(instance);
}
}
In acest fel aplicatzia foloseste tot ceea ce are nevoie fara a sti in realitate cine ii furnizeaza aceste servicii.
Cat costa sa faci toate astea? Poate, si subliniez poate, cateva ore de munca, daca punem la socoteala ca din codul postat am scos gestiunea exceptziilor si motorul de tracing/logging; dar le socotesc niste ore bine fructificate! Unica adevarata problema: sa-l fac pe client sa accepte asta... dar asta e o alta poveste :-)
La post-ul lui Mauro as adauga pentru mai multa claritate ca in Settings.settings avem ServiceProviderType cu valoarea CastleServiceProvider iar in fisierul de configurare avem setat serviciul care implementeaza IService
Tot despre servicii SLP, in acest post scris de Silvano Coriani care lucreaza in Microsoft Italia, dar care in 2002 impreuna cu alti 4 amici (Luca Regnicoli, Paolo Pialorsi, Roberto Brunetti si Marco Russo pe care l-ati cunoscut in post-ul trecut) au fondat DevLeap, o firma cu care fac consultantza si training de calitate si in jurul careia s-a dezvoltat chiar o interesanta comunitate. In 2003 baietzii de la DevLeap au scris primele 4 carti serioase de .NET in italiana, intr-un stil inovativ, complet atipic fatza de ce se gasea in engleza pe piatza (cartea lui Silvano se referea la ADO.NET) si cel putzin pentru mine au jucat in acei ani un rol important, formativ. Post-ul original al lui Silvano e la adresa: http://blogs.msdn.com/scoriani/archive/2008/05/26/microsoft-software-licensing-and-protection-slp-services.aspx
Orice firma care si-a bazat propriul business pe crearea si vanzarea de software si-a pus cel putin o data intrebarea cum sa faca sa-si protejeze proprietatea intelectuala a produsului realizat si sa evite utilizarea sa ilegala. Se estimeaza ca industria software-ului la nivel mondial pierde in jur de 40 miliarde de dolari din cauza piratarii si falsificarii produselor sale, si nu vorbim doar de marile multinatzionale ci si de micile software house sau de system integrator-ii locali care au facut un efort deosebit sa dezvolte si sa puna la punct un produs propriu sau o solutie custom pentru un client sau mai multi clienti. De-a lungul timpului s-au nascut sute de solutii la aceasta problema, unele bazate pe sisteme facute artizanal care pot fi eludate usor, altele bazate pe adevarate platforme de protectie si gestiune a licentzelor de utilizare. Microsoft a intrat pe aceasta piatza cu o solutie dedicata celor care dezvolta cod managed (adica celor care utilizeaza Microsoft .NET Framework ca mediu de executie) numita Microsoft Software Licensing and Protection Services, pe scurt SLP.
Cum functioneaza?
SLP se deosebeste de tehnicile traditzionale de Obfuscation, Encryption sau Code Splitting (cu utilizarea sau neutilizarea unor dispozitive externe de securitate cum ar fi Smart Card sau Secure Memory Stick), introducand conceptul de Secure Virtual Machine (SVM) si de Secure Virtual Machine Language, termeni asimilabili cumva conceptului de "Virtual CPU". Spre deoesebire de tehnicile de encryption a Microsoft Intermediate Language (MSIL), care trebuie sa fie decriptat inainte de a fi executat de catre Common Language Runtime (CLR), SVML-ul este executat direct de catre SVM-ul gazduit de aplicatie, formand o pereche unica diferita pentru orice cheie generata de sistemul de gestiune a licentzelor.
Procesul de protectie al aplicatiei adauga ciclului traditional de dezvoltare software o faza in care, cu ajutorul unui instrument numit SLP Code Protector, vine executata o activitate numita Permutation. Informatziile legate de furnizarea si gestiunea cheilor de licentza sunt date de un produs numit Microsoft SLP Server 2008 sau, in stil Software+Services, de catre serviciile SLP Online Services combinate cu Activation Packs, pachete de licentze unice de activare.
Neexistand biblioteci externe, chei externe, embedded sau altceva similar, devine practic imposibila atat executarea unui reverse engineering cat si violarea termenilor definiti de licentza.
Obiectivul SLP-ului e acela de a pune in balantza necesitatile protectiei proprietatii intelectuale cu acelea legate de performantza in faza de executie a unei aplicatii protejate, din care cauza in faza de definire a protectziei codului este posibil sa se indice care sunt functiile care contzin logica importanta, core business-ul aplicatziei si sa se evite in schimb acele parti din cod care nu au o importantza semnificativa.
De exemplu, la sfarsitul transformarii codul nostru sursa ar putea arata, pentru cine ar incerca sa-l dezasambleze, ceva de genul:
Evident componenta de back-end care se ocupa de generarea si gestiunea licentzelor si a activarilor ofera un set bogat de functionalitati care permit crearea unor versiuni diferite a propriei aplicatii (de exemplu Enterprise, Standard, etc.) permitzand utilizatorilor accesul doar la nivelul de functionalitati achizitzionat, de creare a unor versiuni Trial sau Time Bombed care expira dupa o anumita perioada de timp sau o anumita utilizare din partea clientului, controland activarile executate pentru fiecare cheie si furnizand un raport complet asupra oricarui eveniment trasat de sistem si relativ la operatziile legate de licensing-ul executat de proprii clienti.
In acest moment aplicatia va prezenta utilizatorului final sau celui care va face instalarea un ecran de activare similar celui de mai jos, suportand o multitudine de modele de licensing legate de achizitzionare, utilizare sau upgrade al aplicatiei.
Microsoft SLP permite oricarui business care ocupa de vanzarea/revanzarea de software pachetizat sau de producerea lui la comanda, de a proteja proprietatea intelectuala a propriului produs garantand un corect castig economic din partea propriilor clienti care vor utiliza o modalitate simpla si intuitiva de activare si utilizare a software-ului achizitzionat.
Cu sigurantza limitarea acestei solutii doar la codul managed va starni discutzii, dar aceasta nu e decat inca o confirmare a faptului ca Microsoft promoveaza hotarat propria platforma .NET Framework ca solutzie pentru dezvoltarea de aplicatii pe partea de client, de server sau de dispozitive. Pe piatza exista oricum diversi parteneri (de exemplu Acresso, noii proprietari ai lui InstallShield) care ofera solutzii similare pentru cine dezvolta inca in cod nativ.
La aceasta adresa (http://www.microsoft.com/slps/resources.aspx) puteti gasi o serie de resurse tehnice pentru a va aprofunda cunostintzele pe aceasta tema.
In urmatoarele post-uri veti gasi si cod ;-)
Iata si primul post, scris de Marco Russo, autor a mai multor carti si a mai multor bloguri in italiana si engleza. Caracteristica lui Marco, cel putin asa cum il vad eu, e ca inspira permanent incredere, in tot ceea ce scrie sau face. Il puteti vedea "miscandu-se" in acest video-interviu luat de Cristian Lefter. Post-ul original tradus in continuare il gasiti la adresa: http://blogs.devleap.com/marco/archive/2008/06/04/software-licensing-and-protection-services.aspx
Ieri si azi am avut ocazia sa evaluez caracteristicile unui produs/serviciu Microsoft care cu sigurantza va interesa multe firme din Italia (si nu numai). E vorba de Microsoft Software Licensing and Protection Services care in esentza face doua chestii:
- Protejeaza codul (sau mai degraba o parte a codului) de reverse engineering
- Gestioneaza licensing-ul
In ceea ce priveste protectia codului, mecanismul e urmatorul: se aleg una sau mai multe functii din propriul cod, care sunt transformate din cod IL in cod SVML (Secure Virtual Machine Language). Codul SVML este executabil doar pe o SVM (Secure Virtual Machine), care e specifica fiecarei firme care produce software. Practic, daca o firma producatoare de software, sa-i zicem A, distribuie codul, distribuie si o SVM care va fi diferita de o SVM distribuita de o alta firma producatoare de software, sa-i zicem B. Codul SVML e criptografiat utilizand un algoritm descifrabil doar de catre SVM-ul specific firmei respective. Spre deosebire de IL, codul nu vine compilat de catre JITter ci este interpretat de catre SVM. Aceast mecanism face codul inca si mai sigur decat o compilare in cod managed. Dar fiecare lucru are un pretz, iar aici pretzul e legat de performantze, cu sigurantza inferioare fatza de cele pentru codul IL compilat de JITter si apoi exacutat ca si cod nativ. Aceasta face inacceptabila protejarea in acest mod a intregului cod al unei aplicatii, dar un algoritm criptic/important (ca si proprietate intelectuala) care nu are prea multe implicatii la nivel de performantza e cu sigurantza un bun candidat pentru acest serviciu.
Celalalt serviciu oferit este cel de licensing: SVM-ul leaga functionarea sa de controlul activarii remote care permite controlarea licentzelor activate, furnizarea de versiuni cand e necesar, controlarea fiecarei functii active, etc, etc.
Costuri: deocamdata cine are un abonament MSDN are versiunea Evaluation la dispozitzie iar pentru a merge in productie exista un cost anual de gestiune (nu-mi aduc aminte cat) plus un 1 dolar la activare dar... atentzie! De pe 28 iulie 2008 vor fi modificari semnificative. Asta vrea sa insemne ca mai bine asteptam cateva saptamani pentru ca dupa spusele unor persoane din Microsoft cu care am vorbit, schimbarea va fi foarte importanta.
Pentru a afla mai mult despre acest subiect (in italiana) putetzi citi acest post exhaustiv scris de Silvano Coriani
Urmatorul post ar putea fi pe acelasi subiect dar tratat un pic mai tehnic, sa zicem chiar cel al lui Silvano Coriani (Microsoft Italia), indicat la sfarsit de Marco
M-am decis sa deschid un blog de .NET si in limba romana iar aici pe RONUA mi se pare cel mai potrivit loc, merci Aurelian! ;)
Cateva cuvinte introductive: sunt membru activ al echivalentului italian al RONUA, UGIdotNET, unde continui sa bloghez in italiana chiar daca m-am intors in Romania, am fondat impreuna cu alti 6 amici GUISA (Grupul Italian al Arhitectilor de Solutii .NET), iar de luna trecuta sunt leader local la RONUA Galati. Ca job, administrez proaspat infiintzata filiala R&D in Romania a unei firme italiene, Nexida, unde momentan lucrez cu DSL Tools si VSX si continuam dezvoltarea unui framework orizontal pe CLR si Mono.
Pentru a sparge gheatza, m-am gandit sa incep prin a traduce din italiana cele mai interesante post-uri care apar pe multele comunitati .NET italiene, ca o alternativa la blog-urile in engleza pe care le citim cu totii. Sper sa va fac cunostintza in acest fel cu un spirit extrem de deschis, mai aproape de al nostru decat ce intalnim pe celelalte comunitati internationale .NET. In masura timpului traducerile nu vor fi "chioare", voi incerca sa inserez si un feedback personal si va incurajez sa-l dati si pe al vostru.
Cam asta e ideea cu care incep, vine si primul post foarte curand.