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}
}
}
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". –
@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 –
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 ...) –