Serdecznie Pozdrawiam,
Michał Komorowski
The blog about programming, working in IT and not only
\Tree[.{L0 - Root} [.{L1 - Left child} [.{L2 - Left child} ] [.{L2 - Right child} ]] [.{L1 - Right child} ]]Efekt końcowy wygląda natomiast następująco:
public interface ITest { int Value { get; set; } void Start(); } public class MarshalByRefObjectClass : MarshalByRefObject, ITest { public int Value { get; set; } public void Start() { Console.WriteLine(AppDomain.CurrentDomain.FriendlyName); Value++; } } [Serializable] public class MarshalByValueClass : ITest { public int Value { get; set; } public void Start() { Console.WriteLine(AppDomain.CurrentDomain.FriendlyName); Value++; } }Mamy również następujący kod, w którym testuję jak zachowuja się:
private static void Test(AppDomain app, ITest test, bool doCallBack) { if (doCallBack) app.DoCallBack(test.Start); else test.Start(); Console.WriteLine(test.Value); }A to właściwy test, w którym najpierw tworzę obiekty przekazywane przez wartość/referencję lokalnie i w nowej domenie. Następnie wywołuję dla nich metodę Start i odczytuję właściwość Value.
var app = AppDomain.CreateDomain("TestDomain"); var asm = Assembly.GetExecutingAssembly(); var byRef = new MarshalByRefObjectClass(); var byRef1 = new MarshalByRefObjectClass(); var byRef2 = (MarshalByRefObjectClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByRefObjectClass).FullName); var byRef3 = (MarshalByRefObjectClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByRefObjectClass).FullName); var byValue = new MarshalByValueClass(); var byValue1 = new MarshalByValueClass(); var byValue2 = (MarshalByValueClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByValueClass).FullName); var byValue3 = (MarshalByValueClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByValueClass).FullName); Test(app, byRef, true); Test(app, byRef1, false); Test(app, byRef2, true); Test(app, byRef3, false); Test(app, byValue, true); Test(app, byValue1, false); Test(app, byValue2, true); Test(app, byValue3, false);Pytanie brzmi co zostanie wypisane na ekranie? Pokaż/Ukryj odpowiedź
Sandbox.vshost.exe 1 Sandbox.vshost.exe 1 TestDomain 1 TestDomain 1 TestDomain 0 Sandbox.vshost.exe 1 TestDomain 0 Sandbox.vshost.exe 1
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\Shell\COMMAND_NAME] [HKEY_CLASSES_ROOT\*\Shell\COMMAND_NAME\command] @="COMMAND"Jeśli polecenie ma być dostępne dla określonych plików
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\.EXTENSION\Shell\COMMAND_NAME] [HKEY_CLASSES_ROOT\.EXTENSION\Shell\COMMAND_NAME\command] @="COMMAND"Jeśli polecenie ma być dostępne dla katalogów
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Directory\Shell\COMMAND_NAME] [HKEY_CLASSES_ROOT\Directory\Shell\COMMAND_NAME\command] @="COMMAND" [HKEY_CLASSES_ROOT\Directory\Background\Shell\COMMAND_NAME] [HKEY_CLASSES_ROOT\Directory\Background\Shell\COMMAND_NAME\command] @="COMMAND"W powyższych wzorcach COMMAND_NAME oznacza nazwę opcji jaka pojawi się w menu kontekstowym np.: Shred, EXTENSION to rozszerzenie pliku np.: txt i w końcu COMMAND to skrypt/polecenie jakie ma zostać wykonane po wybraniu przez użytkownika określonej opcji z menu kontekstowego np.: T:\\bin\\shred -n 10 -v -z \"%1\". Zwróćcie uwagę, że przy podawaniu ścieżki stosuję podwójny ukośnik tj. \\ oraz, że cudzysłów wewnątrz polecenia jest poprzedzony ukośnikiem tj. \". Na koniec uzupełnione wzorce należy zapisać w pliku z rozszerzeniem *.reg i przez podwójne kliknięcie na takim pliku dodać go do rejestru. Zmiany będą widoczne natychmiast.
var s = new LineSeries(); s.Title = "Nieciągłe przedziały"; s.Points.Add(new DataPoint(2, 1)); s.Points.Add(new DataPoint(3, 1)); s.Points.Add(new DataPoint(4, 1)); s.Points.Add(new DataPoint(6, 1)); s.Points.Add(new DataPoint(8, 1)); s.Points.Add(new DataPoint(10, 1));To jednak nie zadziała gdyż w rezultacie otrzymamy linię ciągłą, czego zresztą należało się spodziewać ponieważ LineSeries po prostu łączy kolejne punkty. Wypróbowałem więc inne rodzaje wykresów, bawiłem się ustawieniami, ale bez rezultatów. Rozwiązanie okazało się jednak bardzo proste. Jeśli nie chcemy, aby dwa punkty zostały połączone linią to pomiędzy nimi należy umieścić punkt o współrzędnych (Double.Nan, Double.NaN).
var s = new LineSeries(); s.Title = "Nieciągłe przedziały"; s.Points.Add(new DataPoint(2, 1)); s.Points.Add(new DataPoint(3, 1)); s.Points.Add(new DataPoint(Double.NaN, Double.NaN)); s.Points.Add(new DataPoint(4, 1)); s.Points.Add(new DataPoint(6, 1)); s.Points.Add(new DataPoint(Double.NaN, Double.NaN)); s.Points.Add(new DataPoint(8, 1)); s.Points.Add(new DataPoint(10, 1));Na koniec jeszcze przykład tak skonstruowanego wykresu:
var array = Array.CreateInstance(typeof(int), new[] { 10, 10 }, new[] { 5, 5 }); var array2 = (int[,]) array;A teraz mały przykład użycia:
array2[1,3] = 1 // Out of bounds array index array2[5,6] = 1 // OK array2[15,14] = 1 // Out of bounds array index array2[14,14] = 1 // OKOczywiście nie byłbym sobą gdybym nie spróbował tego samego z tablicą jednowymiarową:
var array = Array.CreateInstance(typeof(int), new[] { 10 }, new[] { 5 }); var array2 = (int[]) array;Taka próba rzutowania zakończy się niestety, a może na szczęście, wyjątkiem o treści:
Console.WriteLine(new int[10].GetType()); Console.WriteLine(Array.CreateInstance(typeof(int), new[] { 10 }, new[] { 0 }).GetType()); Console.WriteLine(Array.CreateInstance(typeof(int), new[] { 10 }, new[] { 5 }).GetType()); Console.WriteLine(new int[10,10].GetType()); Console.WriteLine(Array.CreateInstance(typeof(int), new[] { 10, 10 }, new[] { 5, 5 }).GetType());Który wypisze na ekran takie wyniki:
var p = Process.GetProcessById(id); var counter = new PerformanceCounter("Process", "% Processor Time", myProcess.ProcessName); Console.WriteLine("Processor %: {0}", counter.NextValue());Przetestowałem i wyniki okazały się dziwne ponieważ czasami uzyskiwałem zużycie większe niż 100%. Chwila, ale przecież mam 8 rdzeniowy procesor. Spróbujmy więc czegoś takiego:
Console.WriteLine("Processor %: {0}", counter.NextValue() / Environment.ProcessorCount);To już działało dobrze. Postanowiłem jednak poszukać alternatywnego sposobu i przyjrzałem się co oferuje klasa Process. Szybko znalazłem właściwość Process.TotalProcessorTime, która zgodnie ze swoją nazwą zwraca całkowity czas procesora zużyty przez dany proces począwszy od jego uruchomienia. Ja potrzebowałem natomiast aktualnego zużycia. Trochę myślenia, szukania w Internecie (na przykład tutaj) i szybko dopisałem coś takiego:
public class Utils { public class ProcessorUtilizationInfo { public TimeSpan LastProcessorTime { get; set; } public DateTime LastCheck { get; set; } public double Value { get; set; } public Process Process { get; set; } } public static ProcessorUtilizationInfo GetProcessorUtilization(ProcessorUtilizationInfo info) { info.Process.Refresh(); var now = DateTime.Now; var elapsed = now - info.LastCheck; var processorTime = (info.Process.TotalProcessorTime - info.LastProcessorTime); info.LastProcessorTime = info.Process.TotalProcessorTime; info.LastCheck = now; info.Value = processorTime.TotalMilliseconds / elapsed.TotalMilliseconds / Environment.ProcessorCount; return info; } }Dla przejrzystości pominąłem walidację danych. Klasa pomocnicza ProcessorUtilizationInfo jest potrzebna gdybyśmy chcieli wołać metodę GetProcessorUtilization wielokrotnie dla tego samego procesu. Ktoś może marudzić, że używam DateTime.Now, że wynik może być nieprecyzyjny, ale moje testy pokazały, że zastosowanie licznika wydajności i metody GetProcessorUtilization daje podobny rezultaty. Przykład użycia metody GetProcessorUtilization wygląda następująco:
var p = Process.GetProcessById(id); var info = new Utils.ProcessorUtilizationInfo {Process = p}; Console.WriteLine("Processor %: {0}", Utils.GetProcessorUtilization(info).Value * 100);Do opisanych 2 sposobów uzyskania aktualnego zużycia procesora dla wskazanego procesu mam jedno zastrzeżenie. Otóż oba rozwiązania dają co prawda bardzo zbliżone wyniki (zaobserwowałem różnice rzędu 0.1 - 0.2%), ale wyniki te różniły się nawet o 1% od tego co pokazywał menadżer zadań. Ktoś wie dlaczego? Może znacie lepsze rozwiązanie postawionego problemu?
var isValid = true; if (condition_1) isValid = false; if (condition_2) isValid = false;Kod ten działał do momentu, kiedy wprowadzono do niego małą zmianę pokazaną poniżej. Było to pewne uszczegółowienie logiki walidacji danych wejściowych.
var isValid= true; if (condition_1) isValid = false; if (condition_2) isValid = false; //Wprowadzona zmiana else { if (condition_3) isValid = true; else if (condition_4) isValid = false; }Po zastanowieniu się z pewnością stwierdzicie, że coś tutaj nie pasuje. Problem polega na tym, że w określonych warunkach zmienna isValid może zostać z powrotem ustawiona na true nawet jeśli wcześniej stwierdzono, że dane są błędne i ustawiono ją na false!
var isValid = true; if (condition_1) isValid = false; // else if zamiast if else if (condition_2) isValid = false;To nawet jego modyfikacja w opisany wcześniej sposób nie spowodowałaby, że walidator pozwolił na kontynuację przetwarzania pomimo błędnych danych. Może wydawać się, że kilka if'ów zamiast użycia else if nie robi różnicy bo to przecież oczywiste jak ten kod działa. Może i oczywiste, ale tu i teraz. Za kilka tygodni lub miesięcy, dla kogoś innego, może już nie być takie oczywiste. Ktoś inny może również to po prostu przeoczyć.
WITH Test (Id) AS (SELECT NEWID()) SELECT * FROM Test UNION ALL SELECT * FROM Test
<Window x:Class="DrawPlot.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:oxy="http://oxyplot.codeplex.com" xmlns:local="clr-namespace:DrawPlot" Title="DrawPlot" Height="350" Width="525"> <Window.DataContext> <local:MainViewModel/> </Window.DataContext> <Grid> <oxy:Plot Model="{Binding MyModel}"/> </Grid> </Window>Trzeci krok to napisanie klasy MainViewModel, która stworzy i zainicjuje obiekt klasy PlotModel. Obiekt ten będzie zawierał informacje potrzebne do narysowania wykresu i przy pomocy binding'u zostanie przekazany do kontrolki Plot. W podstawowej wersji kod tej klasy jest bardziej niż prosty. Zakładam z góry określony format pliku wejściowego tj. każda linia zawiera jedną liczbę, a część ułamkowa musi być oddzielona od części całkowitej kropką. Użyłem klasy LineSeries czyli rysuję wykres liniowy. W poniższym kodzie celowo pominąłem również bardziej przyjazną obsługę błędów czyli jeśli coś się nie powiedzie to po prostu wyświetlam treść błędu i zamykam aplikację.
namespace DrawPlot { public class MainViewModel { public PlotModel MyModel { get; private set; } public MainViewModel() { try { var args = Environment.GetCommandLineArgs(); var lines = File.ReadAllLines(args[1]); var lineSeries = new LineSeries(); for (var i = 0; i < lines.Length; ++i) lineSeries.Points.Add(new DataPoint(i, Double.Parse(lines[i], CultureInfo.InvariantCulture))); var model = new PlotModel(Path.GetFileName(args[1])); model.Series.Add(lineSeries); MyModel = model; } catch (Exception ex) { MessageBox.Show(ex.ToString()); Application.Current.Shutdown(); } } } }Ostatni krok to dodanie nowej pozycji do menu kontekstowego. Uzyskałem to przy pomocy dodania odpowiedniego wpisu w rejestrze. W moim przypadku skompilowana aplikacja znajduje się w lokalizacji T:\bin\DrawPlot\DrawPlot.exe.
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\Shell\Draw plot] [HKEY_CLASSES_ROOT\*\Shell\Draw plot\command] @="T:\\bin\\DrawPlot\\DrawPlot.exe \"%1\""Narzędzie jest bardzo proste, ale spełnia swoje zadanie. Możne je również łatwo rozbudować na przykład użyć innego rodzaju serii aby narysować wykres kołowy, dodać obsługę innych formatów plików wejściowych itd.
CREATE TABLE #Temp ( Id INT IDENTITY, Value VARCHAR(10) ); BEGIN TRANSACTION; INSERT INTO #Temp VALUES('aaa'); INSERT INTO #Temp VALUES('bbb'); ROLLBACK; SELECT COUNT(1) FROM #Temp; DROP TABLE #Temp;Pokaż/Ukryj odpowiedź
DECLARE @Temp TABLE ( Id INT IDENTITY, Value VARCHAR(10) ); BEGIN TRANSACTION; INSERT INTO @Temp VALUES('aaa'); INSERT INTO @Temp VALUES('bbb'); ROLLBACK; SELECT COUNT(1) FROM @Temp;
SizeLimit | PageSize | Liczba obiektów w AD | Liczba zwróconych obiektów | Uwagi |
0 | 0 | 1580 | 1500 | Brak stronicowania + domyślny limit z serwera |
100 | 0 | 1580 | 100 | Brak stronicowania + limit określony przez nas |
0 | 100 | 1580 | 1580 | Stronicowanie włączone |
200 | 100 | 1580 | 1580 | Stronicowanie włączone + limit określony przez nas |
100 | 200 | 1580 | 100 | Stronicowanie włączone + limit określony przez nas |
maxNumberOfObjects | Mode.Fast | Mode.Normal | Mode.Slow |
100 | 363 | 463 | 7507 |
200 | 407 | 661 | 1600 |
500 | 797 | 2212 | 39323 |
1000 | 1353 | 3060 | 75935 |
public class ADTester { public enum Mode { Fast, Normal, Slow } public void Run(Mode mode, int maxNumberOfObjects) { var ldapPath = "YOUR_LDAP_PATH"; using (var root = new DirectoryEntry(ldapPath)) { using (var searcher = new DirectorySearcher(root) { Filter = "(&(objectClass=user))", SearchScope = SearchScope.Subtree, SizeLimit = maxNumberOfObjects }) { if (mode == Mode.Fast) searcher.PropertiesToLoad.AddRange(new[]{ "displayName","name", "pwdLastSet","userAccountControl" }); using (SearchResultCollection searchResult = searcher.FindAll()) { foreach (SearchResult user in searchResult) { if (mode != Mode.Slow) { var displayName = user.Properties["displayName"]; ... } else { var entry = user.GetDirectoryEntry(); var displayName = entry.Properties["displayName"]; ... } } } } } } }