设置默认(类)值

本文关键字:默认 设置 | 更新日期: 2024-09-24 22:22:12

是否可以为MyClass设置default()值?我的想法是,如果在调用default(MyClass)时返回null,它将返回该类的一个实例,但使用默认值。

例如:

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

所以当我打电话时: nbsp 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)的默认实例。

设置默认(类)值

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

default关键字在那里,因此当使用值类型(不能为null)时,您可以获得默认的对象状态(对于引用类型,为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

如果Foo是抽象的,那么myOtherFoo.myFoo的值可能是什么(除了null)?

但是,让我们更容易,并排除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);

您刚刚创建了一个无休止的初始化循环。

当然,这可以是静态和运行时分析的,也可以是编译时错误(比如在同一结构类型定义中定义结构字段的错误)和运行时异常。。。但你只是剥夺了CCD_ 26有充分记录的行为。

可能的解决方法

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

在您的示例中,这将是:

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

然而,这将不允许返回null。作为一种可能的解决方法,您可以用空接口"标记"不希望返回null的类,并且在类型实现该接口时使用Activator.CreateInstance(),而不是使用new()约束,否则返回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));
    }
}

然后,对于使用该接口"标记"的类,它将创建一个默认实例(使用无参数构造函数:如果未定义任何异常,它将抛出异常),并为所有其他类返回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>();

Fiddle here:https://dotnetfiddle.net/JO8YfV

默认值添加一个类型为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>);
    }
}