一部のタスクはコードを記述するときに非常に一般的であるため、それらを解決するツールは特に便利です。 そして、言語/開発プラットフォームの標準ツールが不十分であると思われる場合、プログラマーは独自の決定を作成します。これは、使用頻度により、お気に入りのバイクになります。 したがって、彼が言語/プラットフォームを変更するときに最初に行うことは、彼のソリューションのローカルな類似物、またはそれらを再現する可能性を探すことです。
C ++ BuilderからC#、WPFへの移行後に発生した日常的な問題を解決するためのヘルパーのコレクションを紹介します。
最初の3つ
public static class IComparableExtensions { public static T Minv<T>(this T value, T maxValue) where T : IComparable<T> { if (value.CompareTo(maxValue) >= 0) return maxValue; return value; } public static T Maxv<T>(this T value, T minValue) where T : IComparable<T> { if (value.CompareTo(minValue) <= 0) return minValue; return value; } public static T Limit<T>(this T value, T minValue, T maxValue) where T : IComparable<T> { if (value.CompareTo(minValue) <= 0) return minValue; if (value.CompareTo(maxValue) >= 0) return maxValue; return value; } }
標準の
Math.Minと
Math.Maxが不快であることが判明したのはなぜですか?
1. Minおよび
Maxの前に
Mathというクラス名を使用する必要がある。 これらの関数を多数含むコードを操作するときは、クラス内で再定義するのが面倒でした。
2. Minおよび
Maxの前に
Mathクラス名を使用する必要があること、およびこれらの関数はこのクラスには存在しないという認識からの不便さ。 ジオメトリを操作するためだけに他の
Math関数が必要でしたが、
Minと
Maxはカウンターとインデックスであり、それだけです! そして、上記の方法の説明は明らかにこれを示しています。
3.ネストが大きい
Min関数と
Max関数の標準表記は、読みにくいと思われます。 むしろ、
最小と
最大の二項演算子が必要です。 上記で定義したメソッドは、必要なものに最も近いものです。
結論として、どの関数を使用して上または下から制限するかを常に把握する努力が必要です。
Limitメソッドは、これらの困難から私を解放します。
C#6では、
System.Mathを使用して記述でき
ます。 、プレフィックスなしの関数を使用します。 ありがとう、でも遅い。
StringMaker
デバッグまたはロギングの場合、多くの場合、値をスペースでリストするだけです。
このために、内部に単純なロジックを持つ
void OutDebug(params object[] args)
ようなメソッドを書くことができます。 ただし、このメソッドに複数のクラスがある場合、別のソリューションが必要です。
public class SM { StringBuilder Sb; SM() { Sb = new StringBuilder(); } SM Add(object value) { if (value==null) return this; var objects = value as IEnumerable; if (objects!=null) { foreach (var obj in objects) Add(obj); } else Sb.Append(value.ToString()); return this; } public override string ToString() { return Sb.ToString(); } public static implicit operator string(SM value) { return value==null ? null : value.ToString(); } public static SM operator +(SM a, object b) { return a.Add(b); } public static SM operator -(SM a, object b) { Sb.Append(' '); return Add(b); } public static SM New { get { return new SM(); } } } public static class IEnumerableExtensions { public static IEnumerable Sep(this IEnumerable objects, object separator) { bool first = true; foreach (var obj in objects) { if (first) first = false; else yield return separator; yield return obj; } yield break; } }
私たちは使用します:
var sm = SM.New+""-1-2-3; var rr = new int[] { 1, 2, 3 }; sm = sm+" ("+rr.Sep(", ")+')'; Trace.WriteLine(sm);
C#6では、文字列内に引数を入力できるようになりました。 ありがとう、でも遅い。
タイマータスク
イベント駆動型の環境では、蓄積されたイベントを処理した後、作業を継続する必要がある場合があります。 通常、作業の中間結果を表示します。
WPFでは、これは直接行うことができます。
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
また、次のクラスを使用して、コードを反復子として設計し、実行できます。
public class TimerTask { public bool IsPaused, IsCancelled; DateTimeOffset NextTime; TimeSpan Interval; Func<bool> Func; static DispatcherTimer Timer; static List<TimerTask> TaskList; TimerTask (double interval, double delay, Func<bool> func) { if (TaskList==null) { TaskList = new List<TimerTask>(); Timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(0.02), IsEnabled = true }; Timer.Tick += Timer_Tick; } TaskList.Add(this); Interval = TimeSpan.FromSeconds(interval); NextTime = DateTimeOffset.Now+TimeSpan.FromSeconds(delay); Func = func; } static void Timer_Tick(object sender, EventArgs ea) { int i = 0, cnt = TaskList.Count; while (i<cnt) { if (TaskList[i].IsCancelled) { TaskList.RemoveAt(i); cnt--; } else { TaskList[i].Tick(); i++; } } } void Tick() { if (IsPaused || DateTimeOffset.Now<NextTime) return; IsCancelled = !Func(); NextTime = DateTimeOffset.Now+Interval; } public static TimerTask DoOnce(Action action, double delay) { return new TimerTask(0, delay, () => { action(); return false; }); } public static TimerTask DoForever(Action action, double interval, double delay) { return new TimerTask(interval, delay, () => { action(); return true; }); } public static TimerTask DoWhile(Func<bool> func, double interval, double delay) { return new TimerTask(interval, delay, () => { return func(); }); } public static TimerTask DoEach(IEnumerable<object> enumerable, double interval, double delay) { var enumerator = enumerable.GetEnumerator(); return new TimerTask(interval, delay, () => { return enumerator.MoveNext(); }); } }
私たちは使用します:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); TimerTask.DoEach(Start(), 0, 0); } IEnumerable<object> Start() { Title = "Starting 1"; yield return null; Starting1(); Title = "Starting 2"; yield return null; Starting2(); Title = "Starting 3"; yield return null; Starting3(); Title = "Started"; yield break; } }
また、このクラスを使用して進捗ダイアログを実装することもできます。
このクラスの明らかな欠点は、タスクの1つで
ShowDialogなどのメソッドを呼び出すと、他のすべての実行がブロックされることです。 各タスクが
DispatcherTimerの独自のインスタンスを持っている場合、これは起こりません。
IntRangeNotifyCollection
このクラスは、頻繁には発生しませんが、大量のデータを表示するプログラムにとって非常に重要な問題を解決します。 仮想化機能を備えた
ListBoxが100,000のコレクションから40のエントリのみを
表示する場合、これらのエントリのうち40だけで十分であると判断するのは自然です。
つまり、
ItemsSourceとして、実際のコレクションではなく、他のクラスを置き換える必要があります。 できる最も軽量。
public class IntRangeEnumerator : IEnumerator { int _Current, _Last; public IntRangeEnumerator(int count) : this(0, count) { } public IntRangeEnumerator(int start, int count) { _Current = start-1; _Last = start+count; } public object Current { get { return _Current; } } public bool MoveNext() { _Current++; return _Current<_Last; } public void Dispose() { } public void Reset() { } } public class IntRange : IList { int _Start, _Count; public IntRange(int count) : this(0, count) { } public IntRange(int start, int count) { _Start = start; _Count = count; } public int Count { get { return _Count; } } public IEnumerator GetEnumerator() { return new IntRangeEnumerator(_Start, _Count); } public object this[int index] { get { return _Start+index; } set { } }
その他の方法 public bool IsSynchronized { get { return true; } } public object SyncRoot { get { return this; } } public void CopyTo(Array array, int index) { for (int i = 0; i<_Count; i++) array.SetValue(_Start+i, index+i); } public bool IsFixedSize { get { return true; } } public bool IsReadOnly { get { return true; } } public int Add(object value) { return 0; } public void Clear() { } public bool Contains(object value) { if (!(value is int)) return false; int i = (int)value; return i>=_Start && i<_Start+_Count; } public int IndexOf(object value) { if (!(value is int)) return -1; int i = (int)value; return i>=_Start && i<_Start+_Count ? i-_Start : -1; } public void Insert(int index, object value) { } public void Remove(object value) { } public void RemoveAt(int index) { }
} public class IntRangeNotifyCollection : IEnumerable, INotifyCollectionChanged { int _Count; public event NotifyCollectionChangedEventHandler CollectionChanged; public IntRangeNotifyCollection() { } public IEnumerator GetEnumerator() { return new IntRangeEnumerator(_Count); } protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (CollectionChanged!=null) CollectionChanged(this, e); } public int Count { get { return _Count; } set { if (value==_Count) return; NotifyCollectionChangedEventArgs e; if (value==0) { e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); } else if (value>_Count) { e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new IntRange(_Count, value-_Count), _Count); } else { e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new IntRange(value, _Count-value), value); } _Count = value; OnCollectionChanged(e); } } }
インデックスをレコードに関連付ける方法 すでにタスクに依存しています。
このテーマに関するHabréの出版物:
»
ObservableCollectionの範囲を持つ機能»
WPFでのデータ仮想化