Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

Παρουσίαση με Ετικέτες

Όλες οι Ετικέτε... » .Net   (RSS)

Silverlight Αttached Βehavior: Φτιάχνοντας ένα Marquee TextBox Control

Έχω ένα Grid στο Silverlight Application και θέλω να κάνω ένα marquee textbox (autoscroll) από δεξιά προς τα αριστερά, μέσα στο κελί του Grid. Τι χρειάζεται να κάνω για να προσθέσω σε ένα control αυτή τη λειτουργία;

Για αρχή έχουμε ένα textbox και στα γρήγορα κάνουμε ένα Translate RenderTransform αλλάζοντας την τιμή, στον άξονα των x από θετικές σε αρνητικές τιμές.

<TextBlock Grid.Row="0" Grid.Column="0" Foreground="#FF2755AF"            FontSize="13.333" x:Name="ScrollText" Text="Really Really Really Really Really Really Large Text" >     <TextBlock.RenderTransform>         <TranslateTransform x:Name="translate" />     </TextBlock.RenderTransform>     <TextBlock.Triggers>         <EventTrigger RoutedEvent="Grid.Loaded">             <BeginStoryboard>                 <Storyboard RepeatBehavior="Forever">                     <DoubleAnimation                         From="400" To="-400"                         Storyboard.TargetName="translate"                         Storyboard.TargetProperty="X"                         Duration="0:0:20" />                 </Storyboard>             </BeginStoryboard>         </EventTrigger>     </TextBlock.Triggers> </TextBlock>

Δυστυχώς όμως έχουμε πολλά προβλήματα όπως φαίνεται. Το κείμενο εκτός του ότι φεύγει τελείως εκτός των bounds του Grid, γιατί έγινε βίαιο translate χωρίς φόβο και πάθος, είναι και clipped ως προς το content. Αν παρατηρείτε μάλιστα, είναι clipped στο μέγεθος του κελιού (0,0). Αυτό γίνεται γιατί το Grid, πριν απεικονίσει τα αντικείμενα που έχει στο visual tree, τα κάνει clip, αυτόματα.

image

Το πρώτο πρόβλημα μπορούμε να το χειριστούμε με clipping στο Grid. Το δευτερο όμως όχι.

<Grid.Clip>     <RectangleGeometry Rect="0,0,100,20"/> </Grid.Clip>

Οπότε θέλουμε να κάνουμε override το clipping του Grid, όπως και τo clipping του textbox. Ο Canvas μας βοηθάει και στις δύο περιπτώσεις, γιατί αφενός αφήνει ελεύθερο το Actual Size του textbox και αφετέρου έχει και αυτό Clipping dependency property ως UIElement.

<Canvas >     <Canvas.Clip>         <RectangleGeometry Rect="0,0,100,20"/>     </Canvas.Clip>     <TextBlock Grid.Row="0" Grid.Column="0" Foreground="#FF2755AF"                 FontSize="13.333" x:Name="ScrollText" Text="Really Really Really Really Really Really Large Text" >     <TextBlock.RenderTransform>         <TranslateTransform x:Name="translate" />     </TextBlock.RenderTransform>     <TextBlock.Triggers>         <EventTrigger RoutedEvent="Grid.Loaded">             <BeginStoryboard>                 <Storyboard RepeatBehavior="Forever">                     <DoubleAnimation                         From="300" To="-300"                         Storyboard.TargetName="translate"                         Storyboard.TargetProperty="X"                         Duration="0:0:20" />                 </Storyboard>             </BeginStoryboard>         </EventTrigger>     </TextBlock.Triggers>     </TextBlock> </Canvas>

Δυστυχώς όμως έχουμε και εδώ μερικά προβλήματα. Καρφωτές τιμές στο Rect και στο From / To του δεν μπορούν να συνδυαστούν με ένα δυναμικό μέγεθος του Text, οπότε για να γίνει το control λίγο reusable χρειάζεται λίγο refactoring.

Η βασική ιδέα είναι να υπολογίζουμε δυναμικά κάποια πράγματα, για να μην χρειάζεται να βάλουμε καρφωτές τιμές. Μπορούμε να το κάνουμε αυτό με ένα πολύ απλό pattern που λέγεται attached behavior. Για να μπορούμε από ένα κεντρικό σημείο να αλλάξουμε ιδιότητες του TextBlock χωρίς να το κάνουμε extend, χρησιμοποιούμε attached property. Ορίζουμε λοιπόν ένα attached property κάπου (στη main page σε εμάς, σε ένα behavior σε άλλες περιπτώσεις), δεν έχει σημασία, για το demo μας, το οποίο θα το δηλώσουμε στο textblock που μας ενδιαφέρει. Θυμίζουμε ότι attached properties είναι properties που ορίζονται σε κάποια κλάση και μπορούν να χρησιμοποιηθούν από άλλες. Τυπικό παράδειγμα το Grid.Row και Grid.Column που βάζουμε στα elements, που περιέχονται σε Grid Panel.

#region MakeScrollable Property public static void SetMakeScrollable(UIElement element, bool value) {     element.SetValue(MakeScrollableProperty, value); } public static bool GetMakeScrollable(UIElement element) {     return (Boolean)element.GetValue(MakeScrollableProperty); } public static readonly DependencyProperty MakeScrollableProperty =     DependencyProperty.RegisterAttached(         "MakeScrollable", typeof (bool), typeof (MainPage),         new PropertyMetadata(false, OnMakeScrollableChanged)); private static void OnMakeScrollableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {     FrameworkElement me = d as FrameworkElement;     me.Loaded += me_Loaded; } static void me_Loaded(object sender, RoutedEventArgs e) {     Clip(sender); } private static void Clip(object sender) {     FrameworkElement me = (FrameworkElement)sender; //Textblock size changed     FrameworkElement parent = (FrameworkElement)me.Parent;     if (GetMakeScrollable(me))     {         parent.Dispatcher.BeginInvoke(()=>         {             parent.Clip = new RectangleGeometry { Rect = new Rect(0, 0, parent.ActualWidth, parent.ActualHeight) };             var translateTransform = new TranslateTransform();             translateTransform.SetValue(NameProperty, "translate");             me.RenderTransform = translateTransform;             Storyboard storyboard = new Storyboard() { RepeatBehavior = RepeatBehavior.Forever};             DoubleAnimation doubleAnimation = new DoubleAnimation()             {                 From = parent.ActualWidth, To = -(me.ActualWidth), Duration = new Duration(new TimeSpan(0, 0, 20))             };             doubleAnimation.SetValue(Storyboard.TargetNameProperty, "translate");             doubleAnimation.SetValue(Storyboard.TargetPropertyProperty,  new PropertyPath("(TranslateTransform.X)"));             storyboard.Children.Add(doubleAnimation);             me.Resources.Add("StoryBoard", storyboard);             storyboard.Begin();         });     } } #endregion

Στη δική μας περίπτωση, ορίζουμε το MakeScrollableProperty. Όταν τεθεί από το textblock, αυτό καλεί την αντίστοιχα ορισμένη OnMakeScrollableChanged που στην ουσία κάνει το clipping.

<Canvas Grid.Row="0" Grid.Column="0" >     <TextBlock SilverlightApplication2:MainPage.MakeScrollable="true"                Text="Really Really Really Really Really Really Large Text "/> </Canvas>

image

Σίγουρα μπορούμε να κάνουμε ακόμα πιο κομψό τo “behavior”, διαχειριζόμενοι και τον canva, αλλά νομίζω ότι ήδη είναι αρκετά reusable για να χρησιμοποιηθεί στα πλαίσια ενός μικρού project. Τέλος, κρατήστε τη βασική ιδέα πίσω από το attached behavior. Αποκτούμε reference στο control που μας ενδιαφέρει μέσα από attached property, κάνοντας hooks και οτιδήποτε, μέσα από PropertyMetadata, αφήνοντας την προσθήκη λειτουργικότητας να γίνει δηλωτικά.

Smile

Posted: Τετάρτη, 20 Οκτωβρίου 2010 4:23 πμ από George J. Capnias | 0 σχόλια
Δημοσίευση στην κατηγορία: , ,

My live Q&A with Eric Lippert, Luke Hoban and Mads Torgersen at Tech·Ed 2010, North America

Yesterday I watched the live 3:30 PM: Languages Panel session, with Eric Lippert, Luke Hoban and Mads Torgersen and the niner Charles Torre, through (http://www.msteched.com/), provided by Channel9 live (http://channel9.msdn.com/). This wasn’t just a live panel, but an interactive QnA too, so as me and a friend / colleague of mine (Nikos), watched it, we asked a question through twitter of Channel9 (http://twitter.com/ch9live). The question (both actually) have been presented through the monitor, to the panel and was answered briefly (check screenshots). The following, are food for thought to everyone and anyone who would like to participate in a conversation about Types in C#, F#, Scala, Java, etc, provide clarifications or error corrections in the way I perceived the answers or else :):)

image

image

Mads Torgersen seemed pretty sure and his initial reaction was original when answering the question! A bright “No”, from the member of the C# Language, designer group (he was speaking of his main domain of work, C#).

Take LINQ for example, he said. LINQ is hardwired in the compiler, providing of this query pattern of composed Select, Where, From, … etc clauses, that provide a single type of computation, over our data. How can one, express through type system, such compositions? The answer is Higher Kind Types. Mads said, that providing such functionality to the C# programmer, would be pretty confusing. There would be rare cases, that one would like to define higher kind types[1], thus this capability (a concept coming from languages like Haskell btw), is not included in future plans currently.

LINQ construct is in general following the monadic [2] programming model. In contrast, F#, being a functional programming language and giving features like computational expressions for asynchronous programming, would be able to provide such level of abstraction if needed in the future. As I understood from what Luke Hoban said, this is imagemore “easy” in F#, as during the design process, such monadic constructs are already implemented in a general way by the F# language design group, hence, this could be exposed in the future.

 

 

The live panel, continued the conversation with very interesting topics, such as Rx, but when the “pure” word came up, I couldn’t resist posting another (and much more impulsive) question, that was answered live very quickly, from the panel.

image

 

 

 

 

 

 

 

 

 

 

 

Luke immediately took action and said that, many people are very active with Haskell inside the MS group, but currently isn’t any movement for incorporating .NET with a pure functional language, due to the incompatible paradigm of current .NET with FP. .NET is a chaos of libraries, doing so many things, providing ways of side effect-“ing” so much, that a pure functional language couldn’t fit in. However ideas from this whole class of functional languages, could migrate in the future, towards F#.

Mads noted something pretty accurate. Purity is also a programming style. This very same concept is very useful as a C# style too. Minimizing side effects, approaches this way of thinking, and we can simulate this in our own way (in a local manner). Mads, also said that this could be the way of .NET library transformation over the years, but not instantly.

I would like to thank the panel, for providing such elaborate answers! Keep up the very good work, providing us such expressive languages!!

[1] Kind is to a type what a type is to a value (I don’t think this is accurate, but it will do the job for now). So, conceptually, we have objects in the bottom. Objects are distinguished by types and types are classified by kinds. Starting again from bottom, objects are known at run time, types must be known at compile time and the kinds are specified by the language itself. Java 5 and C# 2.0 introduced first order parametric polymorphism, thus abstracting over types. The resulting type constructor cannot be further abstracted, however. In a generic way, you can say I want a list of T, where T is int... . So in List<T>, how would it be to abstract over that and specify through a parameter the List too via a Container[T] parameter for example? Thus we could parameterize more (abstract) List<T>, instantiating an M<T, Container> with int and List and getting List<int> or ArrayList<Customer>. So I have the M which takes a type and returns a type. Monads are generalized via this way in Haskell (check Simon Peyton Jones’ words here).

[2] Monad in simple words is a way (strategy), of combining computations to build other computations.

Posted: Πέμπτη, 10 Ιουνίου 2010 2:33 πμ από George J. Capnias | 0 σχόλια
Δημοσίευση στην κατηγορία: , , , , , ,

Closures στη C#

Τελικά τα closures είναι ένα απλό concept, που όταν το κατανοήσει κάποιος, το βλέπει τετριμμένο μετά. Αν μπορούσα να δανειστώ από κάπου, δυό λόγια για ορισμό, νομίζω η wikipedia το περιγράφει αρκετά καλά. Τα closures είναι μία ιδέα των γλωσσών προγραμματισμού, που επιτρέπει, σε συναρτήσεις που αντιμετωπίζονται ως first-class objects, τον συσχετισμό ελεύθερων μεταβλητών τους, με τον λεκτικό περιβάλλοντα χώρο της συνάρτησης. Στη συνέχεια θα ήθελα να διατυπώσω το εν λόγω concept όσο πιο σύντομα γίνεται, δανειζόμενος (μερικώς ή πλήρως) κώδικα από το βιβλίο C# in Depth του Jon Skeet και από το Reference της C#.

First – class functions στην C#

First class functions είναι συναρτήσεις που έχουν όλες τις ιδιότητες των αντικειμένων, μπορούν να δημιουργηθούν δυναμικά, μπορούν να συμμετέχουν σε δομές δεδομένων, να ανατεθούν ως παράμετροι, καθώς και να επιστραφούν από κλήσεις άλλων συναρτήσεων. Στην οικογένεια των γλωσσών προγραμματισμού, η C# ανήκει σε εκείνες που υποστηρίζουν first class functions, μέσω του τύπου δεδομένων Delegate και όλης της πραγματικότητας γύρω από αυτό (anonymous methods, lambdas, expression trees). Θυμίζω ότι σε γενικές γραμμές Delegate είναι ένας τύπος δεδομένων (reference type) όπου παρέχει έναν ασφαλή τρόπο για να ενθυλακώσουμε συναρτήσεις (είτε static είτε instance). Ένα Delegate μπορεί να αναφέρεται μόνο σε συναρτήσεις που είναι συμβατές με το signature που περιγράφει.

Η έννοια των πραγματικών closures, ήρθε στην C# με την έκδοση 2.0, υπό τη μορφή των anonymous delegates (και κατεπέκταση στα lambdas), που σε γενικές γραμμές επιτρέπουν την προσπέλαση (read & write) μεταβλητών που δηλώνονται έξω από το scope αυτών. Χωρίς true closure support, είναι π.χ., η Java και ότι πιο κοντινό έχει σε αυτό το concept, είναι τα anonymous inners classes. Ένα simplistic closure support υπολογίζεται ότι θα υποστηριχθεί με την έλευση της νέας έκδοσης 7, το Σεπτέμβρη 2010.

Περιβάλλον ενός lambda

Στο παρακάτω μπλοκ κώδικα θα δούμε διαφορετικούς τύπους μεταβλητών, αναφορικά, με την θέση τους σε σχέση με το lambda.

Outer/free Variables: Οι μεταβλητές που βρίσκονται στο λεκτικό περιβάλλον του delegate (δηλαδή το scope τους περιλαμβάνει το delegate) θεωρούνται ώς πιθανές μεταβλητές που μπορούν να χρησιμοποιηθούν από αυτό. Ελεύθερες μεταβλητές λοιπόν για ένα lambda, είναι όσες δεν δηλώνονται μέσα στο scope του και όσες δεν δηλώνονται στις παραμέτρους τους (τότε θα λέγονταν bound variables). Μαθηματικώς, για παράδειγμα στον Fourier, για κάποιο χ τα a0, a1, a2, .., bn είναι ελεύθερες μεταβλητές (μπορούν να πάρουν τιμές από τον περιβάλλοντα χώρο) :

image

Όταν ένα free variable δεσμευτεί στο περιβάλλον του lambda, λέγεται ότι είναι ένα από τα Captured Variables, τα οποία είναι εξωτερικές μεταβλητές που χρησιμοποιούνται μέσα στο lambda. Ότι δεν είναι free variable, λέγεται bound variable, όπως για παράδειγμα η μεταβλητή Anonymous Local που ανήκει στις μεταβλητές που δηλώνονται και χρησιμοποιούνται μέσα στο lambda.

int outerVariable = 5; string capturedVariable = "captured"; if (DateTime.Now.Hour==23) {     int normalLocalVariable = DateTime.Now.Minute;           Console.WriteLine(normalLocalVariable); } ThreadStart x = delegate()     {         string anonLocal="local to anonymous method";            Console.WriteLine(capturedVariable + anonLocal);     }; x();

Όταν μέσα σε ένα lambda γίνεται αναφορά σε μία εξωτερική μεταβλητή, λέμε ότι η μεταβλητή αυτή, έχει δεσμευτεί (captured) και η ίδια ζει, όσο ζει και το lambda (γίνεται όπως λέμε, επέκταση του χρόνου ζωής της). Τι συμβαίνει στην πραγματικότητα όταν γίνεται capture; Ας το δούμε μέσα από ένα παράδειγμα.

Έστω ότι έχουμε μία συνάρτηση χωρίς capturing μεταβλητών:

static void F() {     D d = () => { Console.Writeline("No captured variables")}; }

όπου:

public delegate void D();

το delegate.

O compiler αντιμετωπίζει το delegate type instantiation (με το lambda που βλέπετε εκεί), κάνοντας allocation μία πραγματική ενδιάμεση μέθοδο που έχει κάνει generate.

static void F() {     D d = new D(generated_Method); } static void generated_Method() {     Console.Writeline("No captured variables") }

Έστω ότι το lambda κάνει capture μία τοπική μεταβλητή:

static void F() {     int y = 1;     D d = () => { Console.Writeline(y)}; }

H C# σε αυτό το σημείο, προσδίδει σημασιολογία closure και λέει: Επειδή το αντικείμενο που γίνεται reference από το d (η μέθοδος που κάνει Writeline το y, δηλαδή),  (μπορεί) να ζήσει περισσότερο από το scope της F (π.χ., αν επιστραφεί από τη συνάρτηση), κρατά τη μεταβλητή y (όχι απλά την τιμή της), για να την χρησιμοποιήσει, όταν το d γίνει invoke και εκτελεστεί. Πριν δείξουμε όμως την περίπτωση όπου το d ζει περισσότερο από το y (προς το παρόν δεν ζει) ας δείξουμε τι γίνεται σε αυτό το παράδειγμα. O compiler under the hood κάνει το εξής:

void F() {     Generated_Class_Keeping_Environment gen = new Generated_Class_Keeping_Environment();     gen.y = 1;     D d = new D(gen.generated_Method); } class Generated_Class_Keeping_Environment {     public int y;     public void generated_Method()     {         Console.Writeline(y);     } }

Ταξιδεύοντας λίγο περισσότερο στο flow, τι γίνεται αν έχω μεταβλητές σε διαφορετικά statement blocks, όπως στο παρακάτω παράδειγμα; Τότε ο compiler λειτουργεί αναλόγως. Στην παρακάτω περίπτωση, έχουμε αναφορά σε μία εξωτερική μεταβλητή (this.x)

class Top {     int x;     static void F()     {         int y = 1;         for (int i =0; i<10;i++)         {             int z = i*2;             D d = () => {Console.Writeline(x+y+z);};         }     } }

Στην παραπάνω μέθοδο F, το lambda κάνει capture μία μεταβλητή που δηλώνεται μία φορά στο this και δεν αλλάζει, μία τοπική μεταβλητή που δεν αλλάζει και μία τοπική μεταβλητή που αλλάζει συνεχώς, με κάθε iteration. O compiler θα κάνει generate δύο κλάσεις. Μία (Locals2) που θα περιέχει τη μέθοδο, την μεταβλητή Z, καθώς και αναφορά στην άλλη generated κλάση. Η δεύτερη generated κλάση (Locals1) θα έχει τη μεταβλητή y και ένα reference σε object τύπου Top οπότε η συνάρτηση F θα μετασχηματιστεί ως εξής:

void F () {     Locals1 locals1 = new Locals1();     locals1._this = this; //H Top     locals1.y = 1;     for (int i =0; i<10;i++)     {         Locals2 locals2 = new Locals2();         locals2.locals1 = locals1;         locals2.z = i*2;         D d = new D(locals2.method);     }    }

Όπως παρατηρούμε, οι μεταβλητές που έχουν γίνει capture, ζουν τώρα πια στο heap καθώς έχουν γίνει dynamic allocated μέσα σε κλάση.

Ας δούμε όμως, μία χρήση των anonymous delegates σαν κανονικά first order functions που είναι. Διαβάστε προσεκτικά τον παρακάτω κώδικα. Έστω ότι θέλω να ορίσω ThreadStart delegates. Φτιάχνω ένα array από δύο anonymous delegates μέσα στο for και αναθέτω σε κάθε ένα από τα δύο κελιά του array, από ένα anonymous delegate. Τα anonymous delegates έχουν κάνει capture δύο free variables, τις outside και inside (και allocate στο heap με τις compiler generated κλάσεις που είδαμε).

static void Main() {     ThreadStart[] delegates = new ThreadStart[2];     int outside = 0;                                               for (int i=0; i < 2; i++)         {             int inside = 0;                                                delegates[ i] = delegate                                        {                 Console.WriteLine ("({0},{1})",                                    outside, inside);                 outside++;                 inside++;             };         }         ThreadStart first = delegates[0];         ThreadStart second = delegates[1];         first();         first();         first();         second();         second();     } }

Σκεφτείτε ότι την ώρα που γίνεται η ανάθεση τιμής στο delegates[ i] γίνονται οι αναθέσεις, έχουν γίνει capture οι μεταβλητές και ο κώδικας εκτελείται παρακάτω. Γίνεται invoke το delegate της θέσης 0 και τυπώνει (0,0). Τη δεύτερη φορά και τα δύο αυξάνονται, όμοια και την τρίτη φορά και έχουμε (1,1) και (2,2). Τώρα εκτελείται το δεύτερο delegate. To outside είναι σε κατάσταση 3, οπότε με την αύξηση γίνεται 4, το inside όμως έχει ΞΑΝΑ-δηλωθεί (int inside = 0), οπότε το delegate έχει διαφορετικό instance της inside, αρχικοποιημένο στο 0. Οπότε η συνέχεια έπεται ως (3,0) και (4,1). Αν γράφατε τον παραπάνω κώδικα δείτε τι θα σας έδειχνε ο Resharper … ;) Πώς μπορώ να το διορθώσω αυτό, αν δεν το επιθυμώ (χωρίς να πατήσω την λάμπα αριστερά :P);

image

Γιατί είναι τόσο σημαντικά τα closure semantics;

Έστω ότι θέλουμε να φιλτράρουμε μία λίστα με βάση κάποια κριτήρια. Ας το υλοποιήσουμε απλά! Τι χρειαζόμαστε; Μία λίστα για να διαβάσουμε, έναν κανόνα που να δείχνει true αν κάνει pass το αντικείμενο και να θα το εισάγει σε μία νέα λίστα. Αφού διατρέξουμε όλη τη λίστα, θα έχουμε στα χέρια μας μία άλλη λίστα μόνο με όσα “πέρασαν”.

public static IList<T> FindAllItemsBasedOnPredicate<T>(IList<T> src, Predicate<T> predicate)     {         List<T> list= new List<T>();         foreach (T item in src)         {             if (predicate(item))              list.Add(item);                }         return list;     }


Όμορφα. Προχωράμε στο filtering. Έστω ότι θέλουμε να βρούμε όλους τους πελάτες ως 18 ετών.

Στη C# 3.0 (λόγω lambdas) μπορούμε να γράψουμε το εξής:

Predicate<Customer> ageRule = customer => customer.age <= 18; IList<Customer> youngCustomers= FindAllItemsBasedOnPredicate(CustomerList, ageRule );

Αν όμως δεν θέλουμε hard coded το 18 και χρειαζόμαστε να το ρυθμίζουμε μέσα από μία μεταβλητή; Έστω ότι δεν έχουμε closures. Τι κάνουμε; Το αλλάζουμε απλά με μία μεταβλητή; Και αν η μεταβλητή είναι static και αλλάξει; Και αν θέλουμε thread safety; Αν αναφερόμαστε σε Threading και θέλουμε να περάσουμε παραμέτρους στο thread μας; H C# έχει την δυνατότητα capturing μεταβλητής και όχι τιμής και αυτό είναι ένα από τα δυνατά της σημεία.

Some food for thought

Τι θα τυπώσει ο παρακάτω κώδικας χωρίς Copy-Paste-F5; ;););)

delegate void Func(); class Program {     static Func[] functionArray = new Func[10];     static void FillFunctionArray(int count)     {         for (int i = 0; i < count; i++)         {             functionArray[ i] = () =>             {                 Console.Write("{0} ", i);             };         }     }     static void Main(string[] args)     {         FillFunctionArray(functionArray.Length);         for (int i = 0; i < functionArray.Length; i++)         {             functionArray[ i]();         }         Console.ReadKey();                } }

Enjoy closures!!!

Για όσους ενδιαφέρονται να διαβάσουν μία ιδέα για το πώς μπορεί να επεκταθεί η IL για να υποστηρίζονται closures και άλλα όμορφα πράγματα σε επίπεδο IL, μπορούν να διαβάσουν μία σχετική δημοσίευση από MSR του Don Syme, δημιουργού της F#.

 

  1. D. Syme, “ILX: Extending the. NET Common IL for Functional Language Interoperability,” Electronic Notes in Theoretical Computer Science 59, no. 1 (2001): 53–72.
Posted: Τρίτη, 9 Μαρτίου 2010 12:13 πμ από George J. Capnias | 0 σχόλια
Δημοσίευση στην κατηγορία: , , ,

Αντιγραφή αρχείου σε UNC Path προγραμματιστικά (ASP.NET or just code)

Η αντιγραφή αρχείου σε NAS, προγραμματιστικά (ή εν γένει σε κάποιο UNC location εκτός τοπικού μηχανήματος) είναι μία ανάλογη διαδικασία, όπως αν το κάνουμε μέσω του UI. Θα ανοίγαμε το Run, στην συνέχεια πληκτρολογούμε το unc path. Αν σε εκείνη την τοποθεσία έχουν τεθεί permissions για όλους, τότε το παράθυρο ανοίγει κατευθείαν. Σε αντίθετη περίπτωση θα πρέπει να εισαχθούν τα στοιχεία για να γίνει login ο χρήστης, είτε τοπικός, είτε domain (σε αυτήν την περίπτωση θα πρέπει να εισαχθεί και το domain με τον γνωστό τρόπο domain/username). Τι χρειάζεται όμως για να γίνει προγραμματιστικά η διεργασία και μάλιστα μέσα από κώδικα που εκτελείται στο περιβάλλον του IIS; Τότε αυτό που θέλουμε να κάνουμε λέγεται delegation και υπόκειται στο κεφάλαιο ASP.NET Delegation. Με βάση αυτό μπορεί η ASP.NET να προσπελάσει resources σε απομακρυσμένο σύστημα. Μία ειδική περίπτωση αυτού είναι το ASP.NET Impersonation με το οποίο το σύστημα είναι τοπικό. Mε αυτόν τον τρόπο οι ASP.NET εφαρμογές μπορούν να εκτελέσουν κώδικα κάτω από την ταυτότητα του χρήστη τον οποίο απαιτείται να χρησιμοποιήσουν για την πρόσβαση. Για να γίνει αυτό θα πρέπει τα δύο μηχανήματα να μελετηθούν ως προς το λειτουργικό τους, την ύπαρξη του δικαιώματος για delegation, κτλ. Επίσης θέλει προσοχή κατά το σχεδιασμό μιάς τέτοιας λύσης για λόγους ασφαλείας.

Έστω ότι έχω την περίπτωση που ο IIS τρέχει κάτω από το IUSR_machinename για ανώνυμη πρόσβαση. Αν γνωρίζω από πριν username και password τότε μπορώ ρυθμίζοντας το directive <processModel> να ελέγξω την παραπάνω διαδικασία (βλ. link για asp.net delegation). Στην περίπτωσή μου θέλω να ελέγχω την διαδικασία πλήρως χρησιμοποιώντας το LogonUser API. Θέλω λοιπόν να κάνω ένα συγκεκριμένο account login, όπως έδειξα και νωρίτερα όταν ήθελα να μεταφέρω ένα αρχείο από το ui. Δεν υπάρχει αντίστοιχη μέθοδος όμως (ως Login, αυτούσια), στο .ΝΕΤ και η μόνη μέθοδος που κάνει impersonate είναι η WindowsIdentity.Impersonate (IntPtr), από το System.Security.Principal. Τι είναι το IntPtr; Είναι ένα handle για τον χρήστη του συστήματος και ανακτάται από κλήση σε unmanaged κώδικα μέσω της LogonUser από το advapi32.dll.

Στον κώδικά μου ήθελα να μπορώ να τρέχω ένα File.Copy (ή οτιδήποτε) το οποίο θα εκτελείται με βάση συγκεκριμένα credentials.

Impersonation.Execute(myEntity.NasUser, myEntity.NasPassword, () => { //Copy File to NAS    File.Copy(sourceFile, Path.Combine(myEntity.UploadPath, Path.GetFileName(sourceFile)), true); });

 

#region DllImport         [DllImport("advapi32.dll", SetLastError = true)]         public static extern bool LogonUser(             string lpszUsername,             string lpszDomain,             string lpszPassword,             int dwLogonType,             int dwLogonProvider,             out IntPtr phToken             );         [DllImport("kernel32.dll", CharSet = CharSet.Auto)]         public extern static bool CloseHandle(IntPtr handle); #endregion

 

Έτσι η Execute που έγραψα, χοντρικά ήταν η εξής

public static void Execute(string userName, string domain, string password, Action action)         {             try             {                 bool bImpersonated = LogonUser(                     userName,                     domain,                     password,                     logon32LogonInteractive,                     logon32ProviderDefault,                     out tokenHandle);                 if (bImpersonated == false)                 {                     throw new Win32Exception(Marshal.GetLastWin32Error());                 }                 WindowsIdentity newId = new WindowsIdentity(tokenHandle);                 impersonatedUser = newId.Impersonate();                 action();             }             catch (Exception ex)             {                 throw ex;             }             finally             {                 if (impersonation != null)                     impersonation.Dispose();             }         }

Αξίζει να σημειώσουμε ότι στο τέλος θα πρέπει οπωσδήποτε να επαναφέρεται το process identity στο προηγούμενο (μέσω τη undo), γιατί θα υπάρξει πρόβλημα στις περαιτέρω ενέργειες

public void Dispose() {     // Stop impersonating the user.     if (impersonatedUser != null)         impersonatedUser.Undo();     // close handle     if (tokenHandle != IntPtr.Zero)         CloseHandle(tokenHandle); }

Τέλος η παραπάνω διεργασία χρειάζεται full trust (PermissionSetAttribute)

Posted: Πέμπτη, 14 Ιανουαρίου 2010 11:02 μμ από George J. Capnias | 0 σχόλια
Δημοσίευση στην κατηγορία: , ,

Παρουσίαση .NET και C# στο ΠΜΣ Πληροφοριακά Συστήματα ΟΠΑ – Τεχνολογία Λογισμικού

Παραθέτω το υλικό της σημερινής παρουσίασης για .NET, C# που είδαμε στο μάθημα Τεχνολογίας Λογισμικού (ευχαριστούμε τον καθηγητή Εμ. Γιακουμάκη που την πραγματοποιήσαμε επιτυχώς). Η παρουσίαση που διαδέχτηκε τη δική μου, από τον Μιχάλη Ζερβό, βρίσκεται εδώ. Και οι δύο, βρίσκονται στο e-class του μαθήματος.

Στην παρουσίασή μου, προσπάθησα να δώσω όσες περισσότερες ιδέες μπορούσα (στο σύντομο χρονικό διάστημα της μίας ώρας) αναδεικνύοντας την εκφραστικότητά της και τις δυνατότητες που έχει. Καλύφθηκαν αρκετά θέματα, ξεκινώντας από τις βασικές δομές και εξηγώντας στη συνέχεια μία πληθώρα από άλλες όπως, delegates, anonymous functions, anonymous types, iterators, lambdas, extension methods, LINQ καθώς επίσης τα βασικά από τη λειτουργία του ίδιου του περιβάλλοντος εκτέλεσης και της εσωτερικής οργάνωσης του .NET. H παρουσίαση, είχε στόχο να δώσει πολλές ιδέες και ερεθίσματα, καθώς δύσκολα μία έννοια μπορεί να διατυπωθεί μέσα από μερικά slides σε περιορισμένο χρόνο.

Τα Demos (WPF, F#) που παρουσίασα για τις ανάγκες περιήγησης στο Visual Studio βρίσκονται εδώ, καθώς και η παρουσίαση. Για να κατεβάσετε την Beta 2 του VS Studio 2010 Beta 2 θα ακολουθήσετε τον εν λόγω σύνδεσμο. Τέλος, για την σύντομη αναφορά στην F#, μπορείτε να δείτε μία επεξήγηση (στο ίδιο πρόγραμμα για τους πύργους του Ανόι) στο εξής blogpost.

 

Enjoy coding!!

Posted: Τρίτη, 12 Ιανουαρίου 2010 4:43 μμ από George J. Capnias | 0 σχόλια
Δημοσίευση στην κατηγορία: , , ,

Πύργοι του Ανόι σε F#

Θυμάστε πώς είχαμε υλοποιήσει τους πύργους του Ανόι σε C στη σχολή όταν πρωτοπήγαμε; Ορίστε πόσο απλή είναι η έκδοση του σε συναρτησιακό προγραμματισμό (με χρήση της F#).

Κατ’αρχάς μερικές νύξεις στο VS Studio 2010 που έχει ενσωματωμένα templates και debugging tools για αυτή τη νέα γλώσσα. Ανοίγουμε το VS Studio 2010 Beta 2 που μπορούμε να το κατεβάσουμε δωρεάν όσο διαρκεί η φάση Beta και διαλέγουμε να δημιουργήσουμε ένα Console Application σε F#.

image

Στην συνέχεια κάνουμε paste τον παρακάτω κώδικα (προσέξτε το indentation στον κώδικα καθώς έχει σημασία να είναι σωστά στοιχειοθετημένες οι εντολές. Δεν γίνεται compile αν δεν ακολουθηθούν οι αντίστοιχοι απλοί κανόνες) που ορίζει δύο συναρτήσεις, την move (μετακίνησε από στύλο σε στύλο, στην ουσία απλά κάνει print την κίνηση για να βλέπουμε την αλλαγή της κατάστασης) και την hanoi η οποία υλοποιεί την αναδρομική συνάρτηση. Όποτε δοκιμάζω να μάθω την F#, μου αρέσει να στέλνω ότι γράφω στο interactive environment της. Ότι γράφεται σε αυτό το παράθυρο μοιάζει να περνάει από interpreter, αλλά στην ουσία γίνεται δυναμικά compile on-the-fly βλέποντας την αποτίμηση κάποιας έκφρασης, εκείνη τη στιγμή στο παράθυρο.

let move = printfn "Μετακίνησε δίσκο από %c στο %c" let rec hanoi f x t = function   | 1 -> move f t   | n -> hanoi f t x (n - 1)          move f t          hanoi x f t (n - 1)

Το παραπάνω κομμάτι κώδικα ορίζει μία απλή συνάρτηση και μία αναδρομική.

image

Μόλις γίνει η μεταφορά του κώδικα στο interactive παράθυρο, μας παρουσιάζονται δύο γραμμές:

val move : (char -> char -> unit)

//Η δήλωση της move που παίρνει δύο παραμέτρους τύπου char 

//Η παραπάνω δήλωση ερμηνεύεται ως εξής λόγω του currying: το move είναι μία συνάρτηση που παίρνει ένα argument και επιστρέφει μία συνάρτηση με τύπο (char->unit).


val hanoi : char -> char -> char -> int –> unit

//Η δήλωση της hanoi που παίρνει τρεις χαρακτήρες και ένας ακέραιο.

//Ομοιοτρόπως και σε αυτή τη δήλωση. To int σε αυτή τη περίπτωση είναι μία επίδραση του keyword function, το οποίο λόγω της ιδιότητάς του δημιουργεί μία έμμεση μεταβλητή.

Η δήλωση unit σημαίνει ότι δεν επιστρέφεται τίποτα από αυτή τη συνάρτηση. Το unit αποτελεί έναν primitive type που δεν περιέχει καμία απολύτως πληροφορία και η μόνη του τιμή είναι το ().

Πάμε στην αναδρομική συνάρτηση. Ξεκινάμε με το keyword let, με το οποίο δηλώνουμε πως ότι θα γράψουμε, θα δεθεί με το όνομα hanoi, γίνεται δηλαδή binding του ονόματος με την έκφραση της συνάρτησης. Στη συνέχεια, με το keyword rec αναφέρουμε πως το binding αυτό μπορεί να αναφερθεί στον εαυτό του ή πιο απλά, ότι έχουμε μία αναδρομική συνάρτηση. Ορίζονται το όνομα και οι μεταβλητές μας (χωρίς να ορίζουμε τύπο-γίνεται infer από την F#). Με την λέξη κλειδί function ανακοινώνουμε ότι έχουμε μία συνάρτηση η οποία μπορεί να αναγνωρίσει πρότυπα πάνω στα ορίσματά της (pattern matching), έτσι η F# καταλαβαίνει ότι στα ορίσματα υπεισέρχεται ακόμα ένας τύπος ακεραίου (o ακέραιος εκείνος που μας έδειξε το interactive window παραπάνω). Τι είναι pattern matching εν συντομία; Pattern matching είναι η δυνατότητα που προσφέρει η δομή function… και η δομή match…with… να εκτιμά το αποτέλεσμα μίας έκφρασης και να συγκρίνει αυτό το αποτέλεσμα με μία σειρά από κανόνες. Στην περίπτωσή μας, οι κανόνες πηγάζουν από την αναδρομική λύση του προβλήματος. Αν παρατηρήσουμε τη μαθηματική λύση και το παραπάνω κώδικα, θα δούμε ότι οι παραπάνω εκφράσεις είναι η μετάφραση της μαθηματικής λύσης αυτούσια από το συλλογισμό μας, στην F#. Παρατηρήστε την αναδρομική λύση του προβλήματος. Έχει τρία βήματα που λένε:

  • - Μετέφερε n-1 δίσκους από τον πρώτο στύλο στο δεύτερο (οπότε απομένει ο δίσκος με τον αριθμό n – ο μεγαλύτερος στον πρώτο στύλο)
    - Μετέφερε τον δίσκο n από τον πρώτο στύλο στον τρίτο
    - Μετέφερε n-1 δίσκους από τον δεύτερο στύλο στον τρίτο.
  • - Τι γίνεται όταν έχουμε τον δίσκο υπ’ αριθμόν 1 (τον μικρότερο δηλαδή); Μετέφερέ τον στον τρίτο στύλο!

Ακριβώς δηλαδή ότι είναι γραμμένο στο παραπάνω κομμάτι κώδικα!

Εκτελώντας τον κώδικα για διαφoρετικούς ακεραίους, μία για 2 δίσκους και μία για 4 δίσκους, βλέπουμε τη λύση στο interactive window.

image

Cheers!

Posted: Κυριακή, 13 Δεκεμβρίου 2009 4:22 μμ από George J. Capnias | 0 σχόλια
Δημοσίευση στην κατηγορία: , ,

Ασφαλής χρήση FileSystemWatcher

Υπάρχουν σενάρια που χρειάζεται να ξέρετε πότε ένα καινούριο αρχείο δημιουργείται σε έναν κατάλογο, ή πότε γίνεται rename, ή πότε αλλάζει το Last Changed timestamp για να κάνετε διάφορες εργασίες. Έστω το σενάριο, ότι έχετε ένα εξωτερικό εργαλείο που κάνει κάποια λειτουργία μετατροπής αρχείων. Για να μπορείτε να αντιληφθείτε πότε το αρχείο γράφτηκε στο folder που παρατηρείτε και να αντιγράψετε το αρχείο αυτό κάπου αλλού, ή να το επεξεργαστείτε, μπορείτε να χρησιμοποιήσετε το FileSystemWatcher του .NET. Παρόλα αυτά υπάρχει ένα θέμα εδώ. Ο FileSystemWatcher είναι καθαρά για reporting σκοπούς. Αν εσείς χρειάζεστε το αρχείο αυτούσιο, θα πρέπει να γνωρίζετε για το πότε το αρχείο είναι επιπλέον ελεύθερο από οποιοδήποτε άλλο process (π.χ., εκείνου που το αντιγράφει στο folder που κοιτάται). Ένα απλός και γρήγορος τρόπος, για να αποφύγουμε το IOException σε τέτοια περίπτωση είναι ο παρακάτω:

Δημιουργούμε μία συνάρτηση που ελέγχει για το αν το αρχείο είναι κλειδωμένο.

private static bool CheckFileLock(string filename)
{
    try
    {
        using (FileStream inputStream = File.Open(filename, 
                                    FileMode.Open,
                                    FileAccess.Read,
                                    FileShare.None))
        {
            return true;
        }
    }
    catch (IOException)
    {
      //Log maybe
        return false;
    }
}

Στη συνέχεια ο Watcher σας για να δουλέψει θα χρειαστεί τον event handler για το event Created. Ο Handler αυτός θα πρέπει να τσεκάρει ανά τακτά χρονικά διαστήματα (απλή λύση) για τη διαθεσιμότητα του αρχείου και that’s it.

private void onFileCreated(object sender, FileSystemEventArgs e)
{
    DateTime receivedTime = DateTime.Now;
    int delay = ...
    while (true)
    {
        if (CheckFileLock(e.FullPath))
        {
            //Do whatever you want safely
            break;
        }
        TimeSpan elapsedTime = DateTime.Now - receivedTime;
        if (elapsedTime.TotalMinutes > max)
        {
            //Logging definitely 
            break;
        }
        Thread.Sleep(delay);
    }
}

Στην καθυστέρηση που εισάγετε μπορεί κάποιος να χρησιμοποιήσει όποια στρατηγική θέλει, από σταθερό αριθμό ή κάποιο είδους αυξανόμενου.

Posted: Δευτέρα, 12 Οκτωβρίου 2009 1:24 πμ από George J. Capnias | 0 σχόλια
Δημοσίευση στην κατηγορία: ,