私は現在、アプリケーション フレームワークにある重複の一部を減らす過程にあり、次の拡張方法について人々はどう思うでしょうか?
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ThrowExtensions
{
public static T ThrowIfNull<T>(this T instance) where T : class
{
Contract.Ensures(Contract.Result<T>() != null);
if (instance == null)
{
throw new ArgumentNullException("instance", string.Format("Object reference of type '{0}' not set to an instance of an object.", typeof(T).FullName));
}
return instance;
}
}
次の例を考えてみましょう。
すべてを拡張することは良い習慣ではなく、可能であれば避けるべきであることは知っていますが、この場合、きれいではありませんがかなり合理的です。
protected bool Contains<TEntity>(TEntity entity) where TEntity : class
{
Contract.Requires(entity != null);
ObjectStateEntry entry;
bool exist = ((IObjectContextAdapter)_context).ObjectContext.ThrowIfNull().ObjectStateManager.ThrowIfNull().TryGetObjectStateEntry(entity, out entry);
return exist;
}
編集:また、ThrowIfNull を Try() で変更するか、フレームワークの慣習としてこのコンテキストにより適合するものを変更することも考えていますが、それについての意見は本当に好きです。より良い代替案があれば、聞いてうれしいですそれ、ありがとう!
更新:これまでのところ、それが私が最終的に得たものです。
namespace EasyFront.Framework.Diagnostics.Contracts
{
using System;
using System.ComponentModel;
using System.Diagnostics.Contracts;
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ContractsExtensions
{
/// <summary>
/// Calls the code wrapped by the delegate when the subject is not pointing to null, otherwise, <see
/// cref="ArgumentNullException" /> is thrown.
/// </summary>
/// <remarks>
/// Eyal Shilony, 01/08/2012.
/// </remarks>
/// <typeparam name="TSubject"> The type of the subject to operate on. </typeparam>
/// <typeparam name="TResult"> The type of the result to return from the call. </typeparam>
/// <param name="subject"> The subject to operate on. </param>
/// <param name="func"> The function that should be invoked when the subject is not pointing to null. </param>
/// <returns> The result of the invoked function. </returns>
public static TResult SafeCall<TSubject, TResult>(this TSubject subject, Func<TSubject, TResult> func)
where TSubject : class
{
Contract.Requires(func != null);
if (subject == null)
{
ThrowArgumentNull<TSubject>();
}
return func(subject);
}
/// <summary>
/// Calls the code wrapped by the delegate when the subject is not pointing to null,
/// otherwise, <see cref="ArgumentNullException" /> is thrown.
/// </summary>
/// <remarks>
/// Eyal Shilony, 01/08/2012.
/// </remarks>
/// <typeparam name="TSubject"> The type of the subject to operate on. </typeparam>
/// <param name="subject"> The subject to operate on. </param>
/// <param name="func"> The function that should be invoked when the subject is not pointing to null. </param>
public static void SafeCall<TSubject>(this TSubject subject, Action<TSubject> func)
where TSubject : class
{
Contract.Requires(func != null);
if (subject == null)
{
ThrowArgumentNull<TSubject>();
}
func(subject);
}
/// <summary>
/// Ensures that the subject is not pointing to null and returns it, otherwise, <see cref="ArgumentNullException" /> is thrown.
/// </summary>
/// <remarks>
/// Eyal Shilony, 01/08/2012.
/// </remarks>
/// <typeparam name="TSubject"> The type of the subject to operate on. </typeparam>
/// <param name="subject"> The subject to operate on. </param>
/// <returns> The subject. </returns>
public static TSubject SafeReturn<TSubject>(this TSubject subject)
where TSubject : class
{
Contract.Ensures(Contract.Result<TSubject>() != null);
if (subject == null)
{
ThrowArgumentNull<TSubject>();
}
return subject;
}
private static void ThrowArgumentNull<TSubject>() where TSubject : class
{
// ReSharper disable NotResolvedInText
throw new ArgumentNullException("subject", string.Format("Object reference of type '{0}' not set to an instance of an object.", typeof(TSubject).FullName));
// ReSharper restore NotResolvedInText
}
}
}
ここに私がまとめた長所と短所のリストがあります。
長所:
- 複雑な null チェックの冗長性を取り除きます。
- NullReferenceException と比較して、エラーに関する詳細情報を提供します。
- エラーの動作を制御できる一元化された場所。たとえば、本番ビルドでは例外を使用するかどうかを決定でき、デバッグ ビルドではアサートを使用するか、伝播された例外をログに記録するかを選択できます。
- コードははるかに読みやすく、洗練されたものになります。
短所:
- パフォーマンス?
- 特にデリゲート、ラムダ演算子、または関数型プログラミングに慣れていない人にとっては、学習曲線が急です。
- 透明度の低下。
ここに私が対処しなければならなかったいくつかのコードがあります。
var entity = Builder.Entity<User>();
entity.HasKey(u => u.Id);
entity.Property(u => u.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
entity.Property(u => u.DisplayName).IsRequired();
entity.Property(u => u.DisplayName).HasMaxLength(20);
entity.Property(u => u.Username).HasMaxLength(50);
entity.Property(u => u.Password).HasMaxLength(100);
entity.Property(u => u.Salt).HasMaxLength(100);
entity.Property(u => u.IP).HasMaxLength(20);
entity.Property(u => u.CreatingDate);
entity.Property(u => u.LastActivityDate);
entity.Property(u => u.LastLockoutDate);
entity.Property(u => u.LastLoginDate);
entity.Property(u => u.LastPasswordChangedDate);