RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1553354
Accepted
rotabor
rotabor
Asked:2023-11-27 07:23:43 +0000 UTC2023-11-27 07:23:43 +0000 UTC 2023-11-27 07:23:43 +0000 UTC

如何使用 CRTP 在后代类的上下文中初始化基类中的静态变量

  • 772

我决定使用CRTP重做房间中对象的 OOP 实现。

using System;
using System.Linq;
using System.Reflection;
using System.Collections;
using System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Method)]
public class ActionItemAttribute : Attribute {
    public string ActionName { get; set; }
    public string ActionDescription { get; set; }
}
// если сделать реализацию немножко по-другому, то без этого класса можно обойтись
public class ActionItem {
    public IList<CustomAttributeNamedArgument> Attributes { get; init; }
    public string MethodName { get; init; }
    public ActionItem(string mn, IList<CustomAttributeNamedArgument> il) { MethodName = mn; Attributes = il; }
}
class ItemBase {
    public string Name { get; init; }
    public ItemBase(string nm) => Name = nm;
    // это вспомогательная функция, можно и без неё
    protected string GetActionDescription([CallerMemberName] string callingMethod = "")
        => (GetType().GetMethod(callingMethod)!.GetCustomAttribute(typeof(ActionItemAttribute)) as ActionItemAttribute)
            ?.ActionDescription!;
    // это нужно
    public virtual List<ActionItem> ActionableItems {get => null; }
    // это нужно
    public object Act(string action) {
        Console.Write($"    {((ActionItemAttribute)Attribute.GetCustomAttribute(GetType().GetMethod(action), typeof(ActionItemAttribute), true)).ActionName}: ");
        if (GetType().GetMethod(action).ReturnType != typeof(void))
            return GetType().GetMethod(action).Invoke(this, new object[] { });
        else { GetType().GetMethod(action).Invoke(this, new object[] { }); return null; }
    }
    // каждое действие должно быть помечено таки атрибутом, он может быть и пустой,
    // если дополнительная информация не нужна
    [ActionItem(ActionName = "Взять", ActionDescription = "Взять предмет")]
    public virtual void Take() => Console.WriteLine("Не забудь вернуть обратно.");
    [ActionItem(ActionName = "Вытащить содержимое", ActionDescription = "Вытащить содержимое из предмета")]
    public virtual object GetContent() { Console.WriteLine("Аааа, ломать не нужно!!!"); return null; }
}
class Item<TSelf> : ItemBase where TSelf : Item<TSelf> {
    protected static List<ActionItem> _sactionlist;
    // это нужно для построения спика действий
    protected static List<ActionItem> GetActionList(Type T) =>
        T.GetMethods().Where(w => w.CustomAttributes
                .Where(w => w.AttributeType == typeof(ActionItemAttribute)).Any())
            .Select(s => new ActionItem(s.Name, s.CustomAttributes
                .Single(s => s.AttributeType == typeof(ActionItemAttribute)).NamedArguments))
            .ToList();
    public override List<ActionItem> ActionableItems { get => _sactionlist; }
    public Item(string nm) : base(nm) { }
    // это вспомогательная функция, можно и без неё
}
class WindowItem : Item<WindowItem> {
    static WindowItem() => _sactionlist = GetActionList(MethodBase.GetCurrentMethod().DeclaringType);
    public WindowItem(string name) : base(name) { }
    [ActionItem(ActionName = "Открыть", ActionDescription = "Открыть окно")]
    public void Open() => Console.WriteLine(GetActionDescription());
    [ActionItem(ActionName = "Закрыть", ActionDescription = "Закрыть окно")]
    public void Close() => Console.WriteLine(GetActionDescription());
    [ActionItem(ActionName = "Взять", ActionDescription = "Взять предмет")]
    public override void Take() => Console.WriteLine("Ага, попробуй возьми.");
}
class Cloth : Item<Cloth> {
    static Cloth() => _sactionlist = GetActionList(MethodBase.GetCurrentMethod().DeclaringType);
    public Cloth(string name) : base(name) { }
    public void Wash() => Console.WriteLine("Постирать тряпку.");
}
class Box : Item<Box> {
    static Box() => _sactionlist = GetActionList(MethodBase.GetCurrentMethod().DeclaringType);
    public Box(string name) : base(name) { }
    [ActionItem(ActionName = "Вытащить содержимое", ActionDescription = "Вытащить содержимое из коробки")]
    public override object GetContent() => new object[] { "большие солнечные очки", "звонок от велосипеда", "вобла" };
}
class Room : IEnumerable {
    List<ItemBase> _items;
    public Room() => _items = new List<ItemBase>();
    public void Add(ItemBase i) => _items.Add(i);
    public IEnumerator GetEnumerator() => _items.GetEnumerator();
}
internal class Program {
    static void ConsoleWriteLine(object o) =>
        Console.WriteLine(o is IEnumerable ? String.Join("; ", (object[])o) : o);
    static void Main() {
        var room = new Room { new WindowItem("Переднее окно"), new Cloth("Тряпка для доски"), new Box("Шкатулка") };
        foreach (ItemBase itm in room) {
            Console.WriteLine($"Предмет: {itm.Name}");
            foreach (var method in itm.ActionableItems)
                if (itm.GetType().GetMethod(method.MethodName).ReturnType != typeof(void))
                    ConsoleWriteLine(itm.Act(method.MethodName));
                else itm.Act(method.MethodName);
        }
    }
}

我将 _sactionlist 字段移至基类,但我无法摆脱每个后代中的静态构造函数(静态 WindowItem、静态 Cloth、静态 Box)。还有出路吗?

c#
  • 1 1 个回答
  • 57 Views

1 个回答

  • Voted
  1. Best Answer
    VladD
    2023-11-27T20:05:54Z2023-11-27T20:05:54Z

    由于我们在 CRTP 知道派生类的“真实”类型,因此它应该如何工作:

    class Item<TSelf> : ItemBase where TSelf : Item<TSelf> {
        protected static List<ActionItem> _sactionlist = GetActionList(typeof(TSelf));
        // ...
    }
    
    class Cloth : Item<Cloth> {
        public Cloth(string name) : base(name) { }
        public void Wash() => Console.WriteLine("Постирать тряпку.");
    }
    
    • 1

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5