【问题标题】:Nullable indexer param for getter only仅用于 getter 的可空索引器参数
【发布时间】:2022-10-14 23:28:06
【问题描述】:

我有一个 Field<T> 类,它采用 Entity 作为索引器参数:

class Entity { ... }

class Field<T>
{
    T this[Entity? entity]
    {
        get...
        set...
    }
}

indexer getter 应该允许 null Entity 值,而 setter 不应该,如下所示(不幸的是它不能编译):

class Field<T>
{
    T this[Entity? entity] { get... }
    T this[Entity entity] { set... }
}

可能吗?

【问题讨论】:

  • 或者,使用 get-only 属性和 Set 方法。

标签: c#


【解决方案1】:

我不太确定您在示例代码中要做什么。您将Entity 定义为一个类,这使它成为一个引用类型。您可以向编译器指示它是可为空的引用类型,但在内部该类型是相同的。这与值类型不同,后者与? 不同。 (见here。)

所以EntityEntity? 的类型相同,但int -> System.Int32int? -> System.Nullable&lt;System.Int32&gt;

由于索引器的定义方式,请务必注意这一点。 C# 规范说

形式参数列表索引器的定义定义了索引器的签名(第 7.6 节)。具体来说,索引器的签名由其形式参数的数量和类型组成。形式参数的元素类型和名称不是索引器签名的一部分。

Section 14.9 Indexers

你的代码

T this[Entity? entity] { get... }
T this[Entity entity] { set... }

给出编译错误,因为两个签名相同。

您可以通过各种方式解决这个问题,并做出不同的权衡。您始终可以允许 null 并进行运行时检查以引发异常。您可以删除 setter 和 getter 中的一个或两个,并将它们显式实现为方法调用(因此您将丢失 obj[index] 语法)。您可以将Entity 更改为值类型,并使用不同的类型签名实现两个不同的索引器;下面的例子。

namespace IndexerExample
{
    public struct Entity
    {
        public int Id { get; set; }
    }

    public class Field<T>
    {
        private Dictionary<Entity, T> _values = new Dictionary<Entity, T>();

        public T this[Entity? e]
        {
            get
            {
                if (!e.HasValue)
                {
                    return default(T);
                }

                return _values[e.Value];
            }
        }

        public T this[Entity e]
        {
            get
            {
                return _values[e];
            }

            set
            {
                if (_values.ContainsKey(e))
                {
                    _values[e] = value;
                }
                else
                {
                    _values.Add(e, value);
                }
            }
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            var f = new Field<int>();
            var e = new Entity() { Id = 3 };

            f[e] = 9;
            var x = f[null];
            var y = f[e];
        }
    }
}

【讨论】:

    【解决方案2】:

    索引器是不可能的。尝试这个:

    public T this[Entity? entity]
    {
        get
        {
            return this[entity];
        }
        set
        {
            // check for not null
            this[entity] = value;
        }
    }
    

    希望有帮助!

    【讨论】:

    • 明白你的意思,谢谢。这就是我目前的处理方式。顺便说一句,您的代码最终会出现 StackOverflow 异常,因为 getter/setter 都在调用自己。
    【解决方案3】:

    来自here

    [DisallowNull]
    public string? ReviewComment
    {
        get => _comment;
        set => _comment = value ?? throw new ArgumentNullException(nameof(value), "Cannot set to null");
    }
    string? _comment;
    

    在可为空的上下文中,ReviewComment get 访问器可以返回默认值 null。编译器警告必须在访问之前对其进行检查。此外,它警告调用者,即使它可能为 null,调用者也不应将其显式设置为 null。 DisallowNull 属性还指定了一个前置条件,它不影响 get 访问器。当您观察以下特征时,您可以使用 DisallowNull 属性:

    该变量在核心场景中可能为空,通常在首次实例化时。 该变量不应显式设置为 null。

    毫无疑问,您可以使您的索引器适应这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-20
      • 2012-04-14
      • 2013-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多