设置默认(类)值

时间:2015-03-02 04:19:20

标签: c#

我可以为default()设置MyClass值吗?我的想法是,当我调用null时返回default(MyClass),它返回此类的实例,但是使用默认值。

例如:

class MyClass {
     // Default value.
     static public readonly MyClass Default = new MyClass();
}

所以当我打电话时:var defaultValue = default(MyClass);
它类似于:var defaultValue = MyClass.Default;

我需要将它与泛型一起使用:

class DictionaryEx<TKey, TValue>: Dictionary<TKey, TValue> {
    public TValue GetOrNull(TKey paramKey) {
        return ContainsKey(paramKey) ? this[paramKey] : default(TValue);
    }
} 

因此,如果我将GetOrNull()与不存在的密钥一起使用,则不会返回null,而是返回TValueMyClass.Default)的默认实例。

2 个答案:

答案 0 :(得分:1)

这不是default关键字的目标,我很高兴你无法重新定义它:你会让事情变得非常复杂。

default关键字存在,因此当使用值类型(无法为空)时,您将获得默认对象状态(对于引用类型,null和值类型,无参数构造函数状态) )。这是一个明确定义的行为,如果你改变它,你可能会引起很多悲伤。

如果你没有定义一个值类型有一个隐式公共无参数构造函数(所以default(T)实际上可以返回一个值),其中所有字段都被初始化为它们的default()值(如果你想要检查它:用一个带参数的构造函数定义一个struct,没有无参数构造函数,并尝试new myStruct():它编译并工作)。对于引用类型而言并非如此...如果一个类没有明确的公共无参数构造函数(或根本没有定义构造函数),那么你无法在没有参数的情况下实例化它

考虑一下,如果你改变了default()实现,这段代码可以做什么(将默认实现语法作为伪代码)?

public abstract class Foo
{
   protected Foo(int x)
   {
   }      
   protected override Foo default()
   {
     return new Foo(50); // You can't instantiate an abstract class
   }
}
//..
Foo myFoo = default(Foo);

如果default(Foo)是抽象的,那么Foo如何假设实例化default()? 但是为了使它更复杂......想象你有这个值类型,需要 - 能够用public struct Bar { public Foo myFoo; } // var myBar = default(Bar); // this just can't return null since // Bar is a value type 实例化:

null

如果myOtherFoo.myFoo是抽象的,Foo的价值可能是什么(abstract除外?

但是,让我们更轻松一点,让default()无法解决问题(让我们假装它不会让你在抽象类中改变public class Foo { public Bar myBar; protected override Foo default() { return new Foo(); } } public struct Bar { public Foo myFoo; } var myBar = default(Bar); 并给予它一个编译器错误),并考虑这种情况:

default()

您刚刚创建了无限的初始化循环。

这当然可以是静态和运行时分析,并且编译时错误(比如在同一结构类型定义中定义结构字段的那些)和运行时异常都可能被抛出......但是你只是带走了default(T)的记录良好的行为。

可能的解决方法

如果要模拟类中结构的default(T)行为,则只需替换new T()的{​​{1}}并添加T: new()约束。这将要求这些类具有无参数构造函数而不是抽象的,但您可以将其视为&#34; default(T)&#34;如果你愿意实现(没有这两个约束,那么任何涉及创建实例的default()方法都无法工作)。

在您的示例中,这将是:

class DictionaryEx<TKey, TValue>: Dictionary<TKey, TValue>
  where TValue : new()
  {
    public TValue GetOrDefault(TKey paramKey) {
      return ContainsKey(paramKey) ? this[paramKey] : new TValue();
  }
} 

然而,这将禁止返回null。作为一种可能的解决方法,您可以&#34;标记&#34;你不喜欢使用空接口返回null的类,而不是使用new()约束,当类型实现该接口时使用Activator.CreateInstance(),否则返回null ...像:

interface IWithExplicitParameterLessConstructor {}

class DictionaryEx<TKey, TValue>: Dictionary<TKey, TValue>
{
    public TValue GetOrDefault(TKey paramKey) {
        return ContainsKey(paramKey) ? this[paramKey] : 
            (typeof(IWithExplicitParameterLessConstructor).IsAssignableFrom(typeof(TValue)) ? Activator.CreateInstance<TValue>() : default(TValue));
    }
}

然后为课程&#34;标记&#34;使用该接口,它将创建一个默认实例(使用无参数构造函数:它会抛出异常,但是没有定义),并且会为所有其他实例返回null(以及相应的默认值)对于价值类型)。

否则,如果您需要能够拥有default实例值而没有公共无参数构造函数(这在远方闻到),您可以在代码中记录约定,并且使用反射在类类型中查找静态属性值或静态方法。如上所述,这闻起来很远,我绝对不推荐它,但它是可行的:

public static class GetDefaultHelper
{
    public static T GetDefaultInstance<T>()
    {
      var propInfo = typeof(T).GetProperty("Default", BindingFlags.Public | BindingFlags.Static);
      return (propInfo != null) ?  (T)propInfo.GetValue(null) : default(T);     
    }
}

并使用它:

Foo foo = GetDefaultHelper.GetDefaultInstance<Foo>();

在这里小提琴:https://dotnetfiddle.net/JO8YfV

答案 1 :(得分:0)

默认值添加 TValue 类型的新参数。

public class DictionaryEx<TKey, TValue> : Dictionary<TKey, TValue>
{
    public TValue GetValueOrDefault(TKey paramKey, TValue @default)
    {
        TValue value;
        if (TryGetValue(paramKey, out value))
            return value;

        return @default;
    }
}

解决方案2:

public class DictionaryEx<TKey, TValue> : Dictionary<TKey, TValue>
{
    public TValue GetValueOrDefault(TKey paramKey, Func<TValue> funcDefault)
    {
        TValue value;
        if (TryGetValue(paramKey, out value))
            return value;

        return funcDefault();
    }

    public TValue GetValueOrDefault(TKey paramKey)
    {
        return GetValueOrDefault(paramKey, Activator.CreateInstance<TValue>);
    }
}