【问题标题】:Infer Method-Return Type which is generic type of another type推断方法返回类型,它是另一种类型的泛型类型
【发布时间】:2020-05-07 19:24:05
【问题描述】:

我有一个具有一个实现的通用基类:

public abstract class Setting<T> : ISetting
{
    public T DefaultValue { get; }
}

public class SubscribeToNewsletterSetting : Setting<bool>
{
   ...
}

现在我有了一个设置类,使用以下设置:

public class UserSettings
{
    public TSetting GetSetting<TSetting>(int ownerId)
        where TSetting : ISetting
    {
        ....
    }
}

现在我可以像这样使用UserSettings 类:

var setting = settings.GetSetting<SubscribeToNewsletterSetting>(22);
var settingsValue = setting.DefaultValue;

现在我想知道 - 是否可以在不指定类型的情况下一步完成:例如:

var settingsValue = settings.GetSettingDefaultValue<SubscribeToNewsletterSetting>(22);

(我不想这样称呼它)

var settingsValue = settings.GetSettingDefaultValue<SubscribeToNewsletterSetting, bool>(22);

干杯, 曼努埃尔

【问题讨论】:

  • 用户不知道“核心类型”,但知道SubscribeToNewsletterSetting一个?
  • 您能否添加所需用法的(伪代码)示例?我不太清楚你所说的“核心类型”是什么意思。我假设boolclass SubscribeToNewsletterSetting: Settings&lt;bool&gt;?
  • @PeterE - 是的。谢谢,我已经编辑了问题
  • 也许这是个愚蠢的问题,但这将如何编译where TType : ISettingTType 没有定义,只有 T

标签: c# generics methods type-inference


【解决方案1】:

很遗憾,这是不可能的。泛型参数的类型推断是一个全有或全无的命题。可以推断所有参数并且您必须(显式)指定无,或者您必须显式指定它们。

您只能在以下变体之间进行选择:

public class UserSettings
{

    // Variant A
    public TSetting GetSetting<TSetting>(int ownerId)
        where TSetting : ISetting
    {
        return default;
    }

    // Variant B
    public TValue GetSettingValue<TSetting, TValue>(int ownerId)
        where TSetting : Setting<TValue>
    {
        return default;
    }
}

你可以像这样使用它。其中x,y,z 应全部隐式键入为bool

class Demo
{
    public void Run()
    {
        var us = new UserSettings();

        // Variant A
        var x = us.GetSetting<SubscribeToNewsletterSetting>(22).DefaultValue;

        // Variant B 
        var y = us.GetSettingValue<SubscribeToNewsletterSetting, bool>(22);

        // desired, but impossible
        var z = us.GetSettingValue<SubscribeToNewsletterSetting>(22);
    }
}

我的建议是将public T Value {get;} 属性添加到Settings&lt;T&gt;,并将数据库访问代码(或至少其中的强类型部分)添加到“SubscribeToNewsletterSetting”(TValue 类型已知的地方)。

【讨论】:

  • "并将数据库访问代码(或至少其中的强类型部分)添加到 'SubscribeToNewsletterSetting'" - 不要认为这是一个好主意,数据库访问代码应该在另一个级别,假设UserSettings 实例的创建者应该正确指定值,否则你将像 DTO 这样的东西绑定到具体存储的类型,这肯定是不同方面的坏事
  • 我想知道 - 这是什么原因?说到语法,为什么不允许这样的事情呢? TParam Get&lt;TType&gt;(int id) where TType : ISetting&lt;TParam&gt;
  • 好吧,如果有一个系统性的原因,那么为什么它可能就是这样就变得有些明显了。还是烦你。 1) 方法签名必须是完全可定义的(即明确的)。 2) 泛型类型占位符表示允许的差异点。 3)但为了能够实现明确性,必须有某种所有使用占位符的列表;这些基本上是泛型类型参数。这解释了为什么在定义方法时必须全部指定它们。 (另外:否则反射之类的东西将不起作用)。
  • 4) 现在的问题是,为什么我们在使用它们时必须全部指定(或不指定)。我的猜测是:简单。当所有类型都可以推断出来时,一切都很好。但是,例如,如果编译器只能推断出两个中的一个,那么另一个则必须由程序员指定。只是,编译器应该如何知道您是提供了第一个参数还是第二个参数?也许在某些情况下(如果不是大多数情况下)可以推断出这一点,但我相当肯定会有一些边缘情况是不可能的。然后你需要某种语法结构,比如命名类型参数。
  • 如您所见,这种复杂性迅速上升。
【解决方案2】:

如果我理解正确的问题,您想将SubscribeToNewsletterSetting 的实例也视为boolean 值,如果是这样...那么您可以下一步:

    public abstract class Setting<T>
    {
        public T DefaultValue { get; }

        public static implicit operator T(Setting<T> value)
        {
            return value.DefaultValue;
        }
    }

    public class SubscribeToNewsletterSetting : Setting<bool>
    {
    }


    public static async Task Main()
    {
        ...
        // Will work fine.
        bool isEnabled = settings.GetSetting<SubscribeToNewsletterSetting>(22);
    }

【讨论】:

  • @ManuelR.Wenk,所以...您想使用下一行var settingsValue = settings.GetSettingDefaultValue&lt;SubscribeToNewsletterSetting&gt;(22)SubscribeToNewsletterSettingDefaultValue 分配给settingsValue 变量吗?如果是这样,那么我已经编辑了我的答案......
  • 是的,在这种情况下我想收到一个布尔值,当我要求 SomeNameSetting : ISetting 我想收到一个字符串。应推断类型。
  • @ManuelR.Wenk,然后呈现的代码将以这种方式工作
  • @AlexanderToltikov 但前提是您将isEnabled 明确输入为bool。我猜这也是不希望的。
  • @AlexanderTolstikov 我认为这个问题的重点是避免输入bool。只要指定了SubscribeToNewsletterSetting,理论上编译器就应该可以推断出TValue 的类型。它只是不这样做。如果一个方法有两个泛型类型参数,你要么必须同时指定,要么都不指定。即使第二个已经被第一个牵连。我以前也遇到过这种情况,有时很烦人。
猜你喜欢
  • 2011-10-06
  • 2021-06-11
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 1970-01-01
  • 2021-12-25
  • 1970-01-01
相关资源
最近更新 更多