C# 模板函数:不能在“T”中执行非虚拟成员查找,因为它是一个类型参数

C# Template Function: Cannot do non-virutal member lookup in 'T' because it is a type parameter

提问人:MP9 提问时间:5/16/2023 最后编辑:Guru StronMP9 更新时间:5/16/2023 访问量:861

问:

我有一个系统,其中每个接口/类都有两个代码 (, ) - 这是为了有效地从十六进制字符串保存和重新生成对象。InterfaceCodeSubtypeCode

我现在有一个特定的用例,我需要一个继承 . 为了检查 ItemCode 是否与所需的接口/类匹配,我想将值与 和 进行比较(见下文)。可悲的是,这不起作用,因为我无法访问类型参数 () 的成员。IitemT.InterfaceCodeT.SubtypeCodeCS0704

public static T AsType<T>(string ItemCode) where T : Iitem
{
    if (ItemCode.Length < 5) return null;

    ItemCodeDes itemCode = new ItemCodeDes(ItemCode);

    if(itemCode.InterfaceCode != T.InterfaceCode) return null; //Error CS0704 Cannot do non-virtual member lookup in 'T' because it is a type parameter
    
    if(itemCode.SubtypeCode != T.SubtypeCode) return null; //same here...

    //...
}

public static ushort SubtypeCode { get; } = 0;
public static ushort InterfaceCode { get; } = 0;

我真的不会为每个类硬编码查找表(大约有 100-200 个)。

有没有一个好的干净的解决方案来解决我的问题(不使用反射)?

编辑:我仍在使用 C# .NET Core 3.1

对于那些需要更多背景信息的人:

public class Iitem
{
    public static ushort SubtypeCode { get; } = 0;
    public static ushort InterfaceCode { get; } = 0;
    public Iitem(string itemName, string description, ItemTier tier, ushort id)
    {
        Name = itemName;
        Tier = tier;
        Description = description;
        Id = id;
    }

    public string Name { get; }
    public string Description { get; }
    public ItemTier Tier { get; }
    public ushort Id { get; }

    /// <summary>
    /// Gibt den itemspezifischen Code zurück.
    /// </summary>
    /// <returns>Itemcode als Hex-String</returns>
    public virtual string GetItemCode()
    {
        return GenerateItemCode(0, 0);
    }
    internal string GenerateItemCode(ushort Interface, ushort Subtype, ulong Generationspecifics = 0)
    {
        return Interface.ToString("X1") + Subtype.ToString("X1") + Id.ToString("X3") + (Generationspecifics != 0 ? Generationspecifics.ToString("X") : "");
    }

    /// <summary>
    /// Generiert einen String mit den Stats und einer Beschreibung des Items.
    /// </summary>
    /// <returns></returns>
    public virtual string PrintItem()
    {
        return Name + " (" + Tier.ToString() + ") - " + Description;
    }

    public enum ItemTier
    {
        Common,
        Uncommon,
        Epic,
        Legendary,
        Heavengrade,
        Forbidden,
    }
}
private class ItemCodeDes
    {
        public ItemCodeDes(string ItemCode)
        {
            if (ItemCode.Length < 5) return;

            InterfaceCode = ushort.Parse(ItemCode[0].ToString(), System.Globalization.NumberStyles.HexNumber);
            SubtypeCode = ushort.Parse(ItemCode[1].ToString(), System.Globalization.NumberStyles.HexNumber);
            ItemId = ushort.Parse(ItemCode.Substring(2, 3), System.Globalization.NumberStyles.HexNumber);

            if (ItemCode.Length > 5)
                Generationspecifics = ItemCode.Substring(5);
        }
        public ushort InterfaceCode { get; }
        public ushort SubtypeCode { get; }
        public ushort ItemId { get; }
        public string Generationspecifics { get; } = "";
    }
C# 函数 模板

评论

1赞 Guru Stron 5/16/2023
你能提供一个最小的可重复的例子吗?
1赞 vgru 5/16/2023
如果没有,@MP9请提供接口的实际代码。您可能希望在接口中使用成员(在 C# 11 中添加)。Iitemstatic abstract
2赞 Fildor 5/16/2023
等等,是一个吗?Iitem
2赞 canton7 5/16/2023
虽然,我永远不会猜到那是一堂课!!静态抽象属性仅适用于接口,因此可能需要重构Iitem
3赞 canton7 5/16/2023
(这就是为什么包含完整的 MRE 很重要的原因,即使认为您已经包含了所有内容!

答:

2赞 Guru Stron 5/16/2023 #1

有没有一个好的干净的解决方案来解决我的问题(不使用反射)?

有,但它需要您迁移到最新的语言版本。这将启用在接口中使用静态抽象成员的选项。引入相应的接口,然后使用它:

public interface IIitem // probably you will need to use better name
{
    public static abstract ushort SubtypeCode { get; } 
    public static abstract ushort InterfaceCode { get; } 
}

public class Iitem : IIitem
{
   // ...
}

并约束泛型参数:

public static T AsType<T>(string ItemCode) where T : Iitem, IIitem
{
   // ...
}

如果您无法升级以支持新的语言版本,那么您将需要使用反射或重构该方法(如果我正确理解这种情况 - 我通常在类似情况下使用类型的属性,并构建从类型到代码的静态字典映射,反之亦然,因此“缓存”反射)。

评论

0赞 Fildor 5/16/2023
我觉得如果你选择使用(运行时/动态,而不仅仅是一次性启动)反射,你可以吻“这是为了有效地从十六进制字符串中保存和重新生成对象”再见。
0赞 Guru Stron 5/16/2023
@Fildor,您可以“缓存”反射。例如这里。或者在这种情况下,静态字典可能会起作用(需要查看更多代码)。
1赞 Fildor 5/16/2023
至少,你需要做一些“魔术”。这就是我的意思。我想,幼稚的反思不会削减它。
1赞 MP9 5/16/2023
我升级到最新的语言版本,并按照您的建议实现了界面。谢谢!
0赞 Guru Stron 5/17/2023
@MP9很高兴它有所帮助!小 P.S. - C# 泛型不是模板(如果您来自 C++ 背景 - 请查看此文档)