2010-04-15 8 views
8

La mia carriera è iniziata come sviluppatore di paradigma funzionale (LISP) e ora sono uno sviluppatore di rete .net/C#. Certo che sono innamorato di LINQ. Tuttavia, credo anche in (1) utilizzando lo strumento giusto per il lavoro e (2) preservando il principio KISS: degli oltre 60 ingegneri con cui lavoro, forse solo il 20% ha ore di esperienza di LINQ/paradigma funzionale e il 5% avere da 6 a 12 mesi di tale esperienza. In breve, mi sento in dovere di stare lontano da LINQ a meno che non sia ostacolato nel raggiungere un obiettivo senza di esso (in cui la sostituzione di 3 linee di codice O-O con una riga di LINQ non è un "obiettivo").Quando è LINQ (su oggetti) abusato?

Ma ora uno degli ingegneri, con 12 mesi di esperienza LINQ/paradigma funzionale, utilizza LINQ per oggetti, o almeno espressioni lambda, in ogni posizione immaginabile nel codice di produzione. I miei vari appelli al principio KISS non hanno dato alcun risultato. Quindi ...

Quali studi pubblicati posso presentare successivamente? Quali linee guida di "standard di codifica" sono state progettate da altri con un certo successo? Sono stati pubblicati problemi di prestazioni LINQ che potrei indicare? In breve, sto cercando di raggiungere il mio primo obiettivo - KISS - per persuasione indiretta.

Naturalmente questo problema potrebbe essere esteso a innumerevoli altre aree (come l'abuso di metodi di estensione). Forse c'è una guida "uber", molto apprezzata (ad esempio studi pubblicati, ecc.), Che prende una svolta più ampia in questo. Nulla?

EDIT LATE: Wow! Sono stato istruito! Sono d'accordo che sto arrivando a questo punto completamente sbagliato. Ma come chiarimento, per favore dai un'occhiata al codice di esempio che sto effettivamente vedendo. Originariamente ha compilato e lavorato, ma il suo scopo è ora irrilevante. Basta andare con il "sentire" di esso. Ora che sto rivisitando questo campione un anno e mezzo più tardi, sto ottenendo un'immagine molto diversa di quello che è in realtà che mi dà fastidio. Ma mi piacerebbe avere occhi migliori dei miei per fare i commenti.

//This looks like it was meant to become an extension method... 
public class ExtensionOfThreadPool 
{ 
    public static bool QueueUserWorkItem(Action callback) 
    { 
     return ThreadPool.QueueUserWorkItem((o) => callback()); 
    } 
} 

public class LoadBalancer 
{ 
    //other methods and state variables have been stripped... 

    void ThreadWorker() 
    { 
     // The following callbacks give us an easy way to control whether 
     // we add additional headers around outbound WCF calls. 
     Action<Action> WorkRunner = null; 

     // This callback adds headers to each WCF call it scopes 
     Action<Action> WorkRunnerAddHeaders = (Action action) => 
     { 
      // Add the header to all outbound requests. 
      HttpRequestMessageProperty httpRequestMessage = new HttpRequestMessageProperty(); 
      httpRequestMessage.Headers.Add("user-agent", "Endpoint Service"); 

      // Open an operation scope - any WCF calls in this scope will add the 
      // headers above. 
      using (OperationContextScope scope = new OperationContextScope(_edsProxy.InnerChannel)) 
      { 
       // Seed the agent id header 
       OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestMessage; 

       // Activate 
       action(); 
      } 
     }; 

     // This callback does not add any headers to each WCF call 
     Action<Action> WorkRunnerNoHeaders = (Action action) => 
     { 
      action(); 
     }; 

     // Assign the work runner we want based on the userWCFHeaders 
     // flag. 
     WorkRunner = _userWCFHeaders ? WorkRunnerAddHeaders : WorkRunnerNoHeaders; 

     // This outter try/catch exists simply to dispose of the client connection 
     try 
     { 
      Action Exercise =() => 
      { 
       // This worker thread polls a work list 
       Action Driver = null; 
       Driver =() => 
       { 
        LoadRunnerModel currentModel = null; 
        try 
        { 
         // random starting value, it matters little 
         int minSleepPeriod = 10; 
         int sleepPeriod = minSleepPeriod; 

         // Loop infinitely or until stop signals 
         while (!_workerStopSig) 
         { 
          // Sleep the minimum period of time to service the next element 
          Thread.Sleep(sleepPeriod); 

          // Grab a safe copy of the element list 
          LoadRunnerModel[] elements = null; 
          _pointModelsLock.Read(() => elements = _endpoints); 

          DateTime now = DateTime.Now; 
          var pointsReadyToSend = elements.Where 
           (
            point => point.InterlockedRead(() => point.Live && (point.GoLive <= now)) 
           ).ToArray(); 

          // Get a list of all the points that are not ready to send 
          var pointsNotReadyToSend = elements.Except(pointsReadyToSend).ToArray(); 

          // Walk each model - we touch each one inside a lock 
          // since there can be other threads operating on the model 
          // including timeouts and returning WCF calls. 
          pointsReadyToSend.ForEach 
          (
           model => 
           { 
            model.Write 
            (
             () => 
             { 
              // Keep a record of the current model in case 
              // it throws an exception while we're staging it 
              currentModel = model; 

              // Lower the live flag (if we crash calling 
              // BeginXXX the catch code will re-start us) 
              model.Live = false; 

              // Get the step for this model 
              ScenarioStep step = model.Scenario.Steps.Current; 

              // This helper enables the scenario watchdog if a 
              // scenario is just starting 
              Action StartScenario =() => 
              { 
               if (step.IsFirstStep && !model.Scenario.EnableWatchdog) 
               { 
                model.ScenarioStarted = now; 
                model.Scenario.EnableWatchdog = true; 
               } 
              }; 

              // make a connection (if needed) 
              if (step.UseHook && !model.HookAttached) 
              { 
               BeginReceiveEventWindow(model, step.HookMode == ScenarioStep.HookType.Polled); 
               step.RecordHistory("LoadRunner: Staged Harpoon"); 
               StartScenario(); 
              } 

              // Send/Receive (if needed) 
              if (step.ReadyToSend) 
              { 
               BeginSendLoop(model); 
               step.RecordHistory("LoadRunner: Staged SendLoop"); 
               StartScenario(); 
              } 

             } 
            ); 
           } 
           ,() => _workerStopSig 
          ); 

          // Sleep until the next point goes active. Figure out 
          // the shortest sleep period we have - that's how long 
          // we'll sleep. 
          if (pointsNotReadyToSend.Count() > 0) 
          { 
           var smallest = pointsNotReadyToSend.Min(ping => ping.GoLive); 
           sleepPeriod = (smallest > now) ? (int)(smallest - now).TotalMilliseconds : minSleepPeriod; 
           sleepPeriod = sleepPeriod < 0 ? minSleepPeriod : sleepPeriod; 
          } 
          else 
           sleepPeriod = minSleepPeriod; 
         } 
        } 
        catch (Exception eWorker) 
        { 
         // Don't recover if we're shutting down anyway 
         if (_workerStopSig) 
          return; 

         Action RebootDriver =() => 
         { 
          // Reset the point SendLoop that barfed 
          Stagepoint(true, currentModel); 

          // Re-boot this thread 
          ExtensionOfThreadPool.QueueUserWorkItem(Driver); 
         }; 

         // This means SendLoop barfed 
         if (eWorker is BeginSendLoopException) 
         { 
          Interlocked.Increment(ref _beginHookErrors); 
          currentModel.Write(() => currentModel.HookAttached = false); 
          RebootDriver(); 
         } 
         // This means BeginSendAndReceive barfed 
         else if (eWorker is BeginSendLoopException) 
         { 
          Interlocked.Increment(ref _beginSendLoopErrors); 
          RebootDriver(); 
         } 
         // The only kind of exceptions we expect are the 
         // BeginXXX type. If we made it here something else bad 
         // happened so allow the worker to die completely. 
         else 
          throw; 
        } 
       }; 

       // Start the driver thread. This thread will poll the point list 
       // and keep shoveling them out 
       ExtensionOfThreadPool.QueueUserWorkItem(Driver); 

       // Wait for the stop signal 
       _workerStop.WaitOne(); 

      }; 

      // Start 
      WorkRunner(Exercise); 
     } 
     catch(Exception ex){//not shown} 
    } 
} 
+0

Le uniche lamentele che farei su questo frammento di codice non hanno nulla a che fare con la tua domanda originale. 1. Approccio incoerente ai nomi dei locali (dovrebbero essere 'camelCase', ma alcuni sono' PascalCase'). 2. Rileva la classe base universale di "Eccezione". –

+0

@Daniel: Non ti importa di lambda che sono lunghe 140 righe e contengono 3 o 4 livelli nidificati da 20 a 40 lambda di linea, per un metodo complessivo che è/era lungo 200 righe? Hai visto questo post: http://stackoverflow.com/questions/2627662/anonymous-methods-lambdas-coding-standards –

+0

Ci sono due problemi separati: 1. I lambda sono una cosa buona o cattiva (di per sé)? 2. quando è troppo lunga una funzione? Concordo sul fatto che quando una funzione diventa molto lunga e netta può trarre vantaggio dall'essere suddivisa in funzioni separate denominate puramente per chiarezza, quindi è più chiaro quali variabili siano visibili a quali parti di codice. Ma questo è un problema di funzioni lunghe in generale, non specifico per lambda. (Cont ...) –

risposta

15

Beh, sembra a me come se fossi quello che vogliono rendere il codice più complicato - perché ritiene che i colleghi non sono all'altezza del approccio veramente semplice.In molti, molti casi trovo LINQ to Objects rende il codice più semplice - e sì che fa includo cambiando poche righe a uno:

int count = 0; 
foreach (Foo f in GenerateFoos()) 
{ 
    count++; 
} 

diventare

int count = GenerateFoos().Count(); 

per esempio.

Dove non è rendere il codice più semplice, va bene per cercare di allontanarlo da LINQ - ma quanto sopra è un esempio in cui sicuramente non è seriamente ostacolato evitando LINQ, ma il "bacio" il codice è chiaramente il codice LINQ.

Sembra che la tua azienda possa trarre vantaggio dall'addestrare i suoi ingegneri per sfruttare il LINQ agli oggetti, piuttosto che cercare di attirare sempre il minimo comune denominatore.

+1

Sono d'accordo Gli ingegneri devono immergersi nella qualità LINQ e lambda. per tutto, ovviamente, ma non può venire nulla di buono dall'evitarlo per sempre.La compagnia cadrà dietro la curva, come lo saranno i dipendenti. –

+0

Sono venduto - tranne i metodi di estensione BCL preesistenti (come Count() dimostrato) è no il problema per me. Invece, cosa ne pensi di una firma di log che prende deliberatamente solo lambda invece di stringhe: // supponiamo ~ 10000 occorrenze di questo esempio in tutto il codice ... Logger.Log (() => "Hello"); // o ... Logger.Log (() => String.Format ("hey {0}", "there")); Lo scopo è ottenere un aumento di prestazioni (discutibile) se la registrazione è disattivata. –

+3

@Mystagogue: In realtà, stavo parlando con i colleghi di ieri per rinviare la valutazione degli argomenti nelle dichiarazioni di registrazione. È una buona idea quando non ci sono effetti collaterali, in quanto significa che è possibile registrare condizionalmente i dettagli potenzialmente costosi per la formattazione, sapendo che la valutazione non avverrà a meno che non sia abilitata la registrazione dettagliata. Preferisco quello a 'if (Logger.IsEnabled (informazioni)) {Logger.Info (...); } 'che è l'alternativa comune. –

8

Lei sembra essere pari LINQ to Objects con maggiore complessità, perché si assume che l'uso non necessario di viola "keep it simple, stupid".

Tutta la mia esperienza è stata il contrario: si fa complessi algoritmi molto più semplice da scrivere e leggere.

Al contrario, ora mi considerano un imperativo, dichiarazione-based, la programmazione allo stato mutazionale come l'opzione "a rischio" da utilizzare solo quando realmente necessario.

Quindi io suggerirei che si inserisce lo sforzo in ricevendo più dei vostri colleghi per capire il beneficio. È una falsa economia cercare di limitare i tuoi approcci a quelli che tu (e altri) già comprendi, perché in questo settore paga enormi dividendi per rimanere in contatto con le "nuove" pratiche (ovviamente, questa roba è appena nuova, ma come fai notare, è nuovo per molti da uno sfondo Java o C# 1.x).

Per quanto riguarda il tentativo di pin qualche carica di "problemi di prestazioni" su di esso, non credo che si sta andando ad avere molta fortuna. L'overhead coinvolto in Linq-to-objects è di per sé minuscolo.

Problemi correlati