.NET中是否有任何类型指示内部对象是否已初始化?

时间:2014-11-18 01:40:13

标签: c# .net

我知道标题很混乱。以下是我的方案

我的服务合同对象如下所示。

class UpdateRequest
{
   public int EmployeeId { get; set; }
   public string EmployeeName { get; set; }
   public decimal Salary { get; set; }
}

现在,上面类的契约对象被传递给更新数据库中记录的方法。假设我想用Id:33更新员工,但我只想更改其名称,我想单独留下薪水。

一种方法是不要担心这些细节并更新所有内容,但这需要我服务的客户端也传递Salary值,以免被覆盖到默认(十进制)值。

我能想到的另一种方法是创建以下类型

public class TrackedValue<TInternal> : where TInternal:new
{
  private TInternal value = default(TInternal);
  public TInternal Value { get; }
  private bool initialized;

  public static implicit operator TrackedValue<TInternal>(TInternal v)
  {
     return new TrackedValue<TInternal> { value = v, initialized = true };
  }
}

还有其他方法吗? .NET没有上面已有的东西吗?我假设如果他们有像Nullable这样的东西他们也必须为这个问题找到一些东西。

Nullable类型不会帮助我。在某些用例中,我必须将某些table-column的现有值更改为null。那会把它扔掉。

更新:有些人可能会说,我的服务应该针对该类型的默认值检查属性值。 &#39; bool&#39;的默认值是假的,如果我的方法被调用bool属性值为false,它将破坏我的逻辑。

2 个答案:

答案 0 :(得分:4)

你的TrackedValue课程非常适合。但是,你有点重新发明了轮子。这被称为Maybe<T> monad。

查看“Maybe”monad,你会得到很多细节,但这里是:

public class Maybe<T>
{
   public readonly static Maybe<T> Nothing = new Maybe<T>();

   public T Value { get; private set; }
   public bool HasValue { get; private set; }

   public Maybe()
   {
       HasValue = false;
   }

   public Maybe(T value)
   {
       Value = value;
       HasValue = true;
   }
}

它非常类似于可空类型,但它不仅限于值类型。

所以你可以拥有这个:

class UpdateRequest
{
   public int EmployeeId { get; set; }
   public Maybe<string> EmployeeName { get; set; }
   public Maybe<decimal> Salary { get; set; }
}

例如,如果您只是尝试更新薪水,那么您可以像这样初始化类:

var request = new UpdateRequest()
{
    EmployeeId = 1234,
    EmployeeName = Maybe<string>.Nothing,
    Salary = new Maybe<decimal>(50000m),
};

或者,您可以像这样定义.ToMaybe()扩展方法:

   public static Maybe<T> ToMaybe<T>(this T value)
   {
       return new Maybe<T>(value);
   }

然后初始化看起来像这样:

var request = new UpdateRequest()
{
    EmployeeId = 1234,
    EmployeeName = Maybe<string>.Nothing,
    Salary = 50000m.ToMaybe(),
};

或者你可以像这样添加一个隐式运算符:

   public static implicit operator Maybe<T>(T v)
   {
       return v.ToMaybe();
   }

现在初始化更简单:

var request = new UpdateRequest()
{
    EmployeeId = 1234,
    EmployeeName = Maybe<string>.Nothing,
    Salary = 50000m,
};

然后你应该为类型定义两个“绑定”运算符 - 在linq中也称为SelectMany - 就像这样:

  public static Maybe<U> Select<T, U>(this Maybe<T> m, Func<T, U> k)
   {
        return m.SelectMany(t => k(t).ToMaybe());
   }

    public static Maybe<U> SelectMany<T, U>(this Maybe<T> m, Func<T, Maybe<U>> k)
   {
       if (!m.HasValue)
       {
           return Maybe<U>.Nothing;
       }
       return k(m.Value);
   }

   public static Maybe<V> SelectMany<T, U, V>(this Maybe<T> m, Func<T, Maybe<U>> k, Func<T, U, V> s)
   {
       return m.SelectMany(x => k(x).SelectMany(y => s(x, y).ToMaybe()));
   }

现在你可以针对可能的值进行LINQ查询。

Maybe<decimal> tax =
    from salary in request.Salary
    select salary * 0.1m;

然后,如果request.SalaryNothing,那么tax将为Nothing。否则将执行计算。

我希望这会有所帮助。

答案 1 :(得分:1)

一个选项是,您可以将功能推送到更新方法,而不是在您的类中使用,而是让用户指定要更新的列。

我在过去实现过这个问题时,当我们在执行大型查询时遇到了perf问题,并且我们正在传递一些大型对象,其中包含不必要的数据(我们有一个很少改变或需要的巨大描述字段)。 / p>

public bool UpdateObjects(List<UpdateRequest> objects, List<string> propertiesToUpdate)
{
    // code here to update objects matched on primary key field, 
    // and set only the specified properties (using reflection)
}

我们还围绕此创建了一些易于使用的包装器,它提供了一个排除“庞大”属性的选项,例如:

public bool UpdateObjects(List<UpdateRequest> objects, bool includeDescription)

有一个基于反射设置属性的示例:Setting a property by reflection with a string value