【问题标题】:Declaring a variable dynamically [closed]动态声明变量[关闭]
【发布时间】:2017-04-09 10:13:03
【问题描述】:

我有这样一个简单的用户结构:

public Guest() : IUser
{
}

[Table("Users")]
public class Customer : Guest
{
    [Key]
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Address { get; set; }
    public bool IsAdmin { get;protected set; }
    public UserCart UserCart { get; private set; }

    public Customer()
    {

    }

    public Customer(string username, string password, string address)
    {
        this.Address = address;
        this.UserName = username;
        this.Password = password;
        this.IsAdmin = false;
        this.UserCart = new UserCart();
    }
}

public class Admin : Customer
{
    public Admin()
    {

    }

    public Admin(string username, string password, 
        string address) 
        : base(username, password, address)
    {
        IsAdmin = true;

    }
}

继承是基于他们可以做的Actions。该系统必须根据使用它的人而有所不同。

管理员可以浏览、购买和处理订单和商品。

客户可以浏览和购买。

客人只能浏览(当然还有登录/注册)。

要知道谁是我们当前的用户,我们需要在Authentication class 中添加一个属性或字段,即IUser,这样任何人,管理员、客户和访客都可以在其中实例化。 问题是,当我只有这个 IUser 时,当我实例化时,我基本上什么也做不了。

所以问题是:我想加载该字段或属性,但我仍然想访问它的功能 - 但只有给定用户可以访问的功能,所以如果管理员登录,我想做管理员的工作。如果客户登录,我只希望能够列出商品等。这个怎么做?

编辑:尝试反射

public class Guest : IUser
    {
        public Guest()
        {

        }

        public object Get()
        {
            Type type = this.GetType();
            var result = Activator.CreateInstance(type);
            return result;
        }
    }

Get() 方法只给出一个object,但如果它被Guest 调用,它必须是Guest。如果由Admin 调用,它必须是管理员。 我可以使用覆盖来做到这一点,但以后很难修改。

所以我希望这样:

(MyObject)Activator.CreateInstance(type);

如果从访客调用,MyObject 是访客,如果从管理员调用,则为管理员。我错过了什么?当然我尝试了泛型,但是当我实现接口时需要一个具体的类型,所以基本上我需要在我所有的后代类中实现它。

【问题讨论】:

  • 如果你想在你的情况下使用这个方案,正如你已经指出的那样,是行不通的。您需要更改类的继承方式。你不这么认为吗?我认为IUser 需要一个公共方法Login
  • 感谢您与我们联系。这不仅取决于我,还有一大堆其他方法,我只是想在这里保持简单。我的想法是,我将在IUser 中定义的方法中使用reflection,比如Get(),它将返回变量的实际类型。可行吗?
  • 我已经在我们制作的一些游戏中使用了它。基本上,我们有Monster 类和User 类,它们派生自Unit。我在基类中有一个方法GetUnitType()defiend。它也很快:)
  • 很高兴我们想到了同样的事情。你能告诉我你在那里是怎么做的吗?在我的课程中使用 if-elseif 等是很明显的,但我需要一些通用的东西,我现在所能做的就是使用Activator.CreateInstance(typeOfMyObject),但我必须将它转换为我的类型。或者我应该在我的所有课程中覆盖这个Get()
  • “问题是,当我只有这个 IUser 时,当我实例化时,我基本上什么都做不了。” – 这些用户类型有什么共同点?这就是通用接口中应该包含的内容。如果它们没有任何共同点,那么它们可能根本就不共享相同的基类型。

标签: c# backend code-organization


【解决方案1】:

根据我们在 cmets 中的讨论,这里是我们服务器端游戏代码的直接复制粘贴。但要记住一个提醒:如果您定义了IUser 接口,但之后总是将其转换为适当的类,例如var customer = iUser as Customer。你可能走错了路。我们只在少数情况下使用下面的代码。

public void DoSomethingWithBattleUnit(BattleUnit bUnit){
    if(bUnit.UnitType==typeof(BattleMonster)){
    var bMonster = bUnit as BattleMonster;
    bMonster.ActivateAI();
}


public abstract class BattleUnit {
    public abstract Type UnitType { get; }
}

public class BattleMonster : BattleUnit {
    /// <summary>
    /// Current type for this class.
    /// </summary>
    /// <remarks>Micro optimization instead of using this.GetType() or calling typeof(BattleMonster) each time.</remarks>
    static readonly Type Type = typeof(BattleMonster);

    public override Type UnitType => Type;
}

public class BattleUser : BattleUnit, IUser {

    /// <summary>
    /// Current type for this class.
    /// </summary>
    /// <remarks>Micro optimization instead of using this.GetType() or calling typeof(BattleUser) each time.</remarks>
    private static readonly Type Type = typeof(BattleUser);

    public override Type UnitType => Type;
}

【讨论】:

  • 感谢您的回答。我无法理解的是我将如何从中实例化?我用我正在尝试的一些代码更新我的问题,等一下......
  • 你为什么要这样做?我不明白为什么一个实例应该有一个额外说明它是什么类型的成员。那你在做if (someUnit.UnitType == typeof(BattleMonster))吗?不要那样做。只需执行 if (someUnit is BattleMonster) 即可。对象已经有一个实际的类型。
  • @poke 是的,我们在极少数情况下确实做到了if (someUnit.UnitType == typeof(BattleMonster))。很少有使用 is 的情况,因为它会进行强制转换,并且在大多数情况下,您最终还是会强制转换变量。
  • is not 转换变量。如果你打算无论如何进行转换,那么你应该只用as进行类型转换并检查null。或使用new pattern variables with is in C#7
  • 顺便说一句,您的obj.UnitType 属性只是obj.GetType() 的一种脆弱方式。您在那里添加了很多样板代码,这些代码迟早会主动造成伤害。它过多地依赖于人类记住正确实现它,并且对整个继承有很多影响。想想现在有一个BattleMonster 的子类型(只是一些特殊的):开发人员不仅需要记住主动覆盖该属性,还必须更改每个只期望 normal 的逻辑BattleMonster 而不是在这里使用继承来接受任何种类。
【解决方案2】:

这实际上是可能的,甚至没有那么难。

解决方案 1:dynamic 关键字

我有这个接口IUser 和其中的一个属性

public interface IUser
{
    dynamic Instance { get; }
}

仅在祖先中实现它,在我的例子中是访客:

public class Guest : IUser
{
    public Guest()
    {

    }

    public dynamic Instance
    {
        get
        {
            return this;
        }
    }
}

这样我就可以在 Program.cs 中像这样声明一个IUser

 IUser user = new Admin("wat", "asd", 012);
 Console.WriteLine(user.Instance.PW);

我得到的密码是“asd”。

市长缺点:

由于动态,我无法使用 Intellisense。

解决方案 2:使用扩展方法

这个实现和使用都有点难看,但仍然可以利用 Intellisense 的强大功能。

所以这里我们有这个很好的扩展方法:

public static class Extensions
{
    public static T Get<T>(this T input) where T:class
    {
        Type t = input.GetType();
        MethodInfo method = t.GetMethod("Get");
        MethodInfo generic = method.MakeGenericMethod(t);
        return (T)generic.Invoke(input,null);
    }
}

这要求所有类都有一个Get 方法。 就我而言,我将在我的 IUser 界面中定义它,如下所示:

public interface IUser
{
    T Get<T>() where T : class;
}

如果我以后要使用 as 关键字,T 必须是一个类。

Get 在我的 Guest 中的实现就像

public T Get<T>() where T:class
{
     var _instance = this;
     return _instance as T;
}

当然不需要子孙再次实现Get

一旦完成,我们就可以得到当前类型:

 IUser adm = new Admin("wut", "mehpw", 1000);
 var stuff = adm.Get<Admin>().Name;

缺点:

我确实必须知道我正在使用的对象的类型,所以基本上这不过是使用

var stuff = (adm as Admin).Name;

所以这里几乎没有任何边缘。

我可以考虑加载具有给定属性值的类,但这与我创建的 IUser 的引用不同,而且肯定会非常慢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多