是否可以使用类型变量强制转换对象的变量?

时间:2015-02-08 18:10:27

标签: c# casting

我正在尝试做的是我正在开发的游戏我有一个使用电报结构的消息传递系统如下:

public struct Telegram
{
    private int sender;
    private int receiver;
    public int Receiver
    {
        get
        { return receiver; }
    }

    //Message of an enumerated messageToSend in Messages
    private Message messageToSend;
    public Message MessageToSend
    {
        get
        { return messageToSend; }
    }

    //for delayed messages
    private double dispatchTime;
    public double DispatchTime
    {
        get
        { return dispatchTime; }
        set
        { dispatchTime = value; }
    }

    //for any additional info
    private object extraInfo;
    public object ExtraInfo
    {
        get
        { return extraInfo; }
    }

    public Telegram(double time, int otherSender, int otherReceiver,
                Message otherMessage, object info = null)
    {
        dispatchTime = time;
        sender = otherSender;
        receiver = otherReceiver;
        messageToSend = otherMessage;
        extraInfo = info;
    }
}

我希望能够做的是因为额外的信息是基于消息类型传递的东西,并且需要是对象,为了方便不必使用各种额外信息类型编写一堆函数,我想要在函数传递额外的info变量时获取其装箱的对象的类型。

我知道我可以使用.getType()执行此操作并将其存储在Type变量中。

这是一个棘手的部分,我不确定我能不能做到。我想要做的是使用Type变量来在接收电报的事物根据发送的消息类型处理它时转换对象。这可能吗?

不能在电报类中使用泛型,因为当我试图转换我的消息代码时它会导致事情中断。以下是相关代码的其余部分:

    /*Telegrams are stored in a priority queue. Therefore the < and ==
    operators are overloaded so the PQ can sort the telegrams
    by time priority. Times must be smaller than 
    SmallestDelay before two Telegrams are considered unique.*/
    public const double SmallestDelay = 0.25;

    public static bool operator ==(Telegram t1, Telegram t2)
    {
        return (Math.Abs(t1.dispatchTime - t2.dispatchTime) < SmallestDelay) &&
         (t1.sender == t2.sender) &&
         (t1.receiver == t2.receiver) &&
         (t1.messageToSend == t2.messageToSend);
    }

    public static bool operator !=(Telegram t1, Telegram t2)
    {
        return (Math.Abs(t1.dispatchTime - t2.dispatchTime) > SmallestDelay) &&
         (t1.sender != t2.sender) &&
         (t1.receiver != t2.receiver) &&
         (t1.messageToSend != t2.messageToSend);
    }

    public static bool operator <(Telegram t1, Telegram t2)
    {
        if (t1 == t2)
            return false;
        else
            return (t1.dispatchTime < t2.dispatchTime);
    }

    public static bool operator >(Telegram t1, Telegram t2)
    {
        if (t1 == t2)
            return false;
        else
            return (t1.dispatchTime > t2.dispatchTime);
    }

sealed class MessageDispatcher
{
    public const double sendMessageImmediately = 0.0;
    public const int noAdditionalInfo = 0;
    public const int senderIdIrrelevant = -1;

    //a set is used as the container for the delayed messages
    //because of the benefit of automatic sorting and avoidance
    //of duplicates. Messages are sorted by their dispatch time.
    private static SortedSet<Telegram> priorityQueue = new SortedSet<Telegram>();

    /// <summary>
    /// this method is utilized by DispatchMessage or DispatchDelayedMessages.
    /// This method calls the messageToSend handling member function of the receiving
    /// entity, receiver, with the newly created telegram
    /// </summary>
    /// <param name="receiver"></param>
    /// <param name="messageToSend"></param>
    private static void Discharge(ref BaseEntityInfo receiver, ref Telegram message)
    {
        if (!receiver.HandleMessage(ref message))
        {
            //telegram could not be handled
        }
    }

    private MessageDispatcher() { }

    public static readonly MessageDispatcher instance = new MessageDispatcher();

    /// <summary>
    /// given a messageToSend, a receiver, a sender and any time delay, this function
    /// routes the messageToSend to the correct entity (if no delay) or stores it
    /// in the messageToSend queue to be dispatched at the correct time. Entities referenced 
    /// by iD.
    /// </summary>
    /// <param name="delay"></param>
    /// <param name="sender"></param>
    /// <param name="otherReceiver"></param>
    /// <param name="messageToSend"></param>
    /// <param name="additionalInfo"></param>
    public static void DispatchMessage(double delay, int sender,
                            int otherReceiver, Message message,
                            object additionalInfo = null)
    {
        //get the reciever
        BaseEntityInfo receiver = EntityMgr.entityManager.GetEntityFromID(otherReceiver);

        //make sure the Receiver is valid
        if (receiver == null)
            return;

        //create the telegram
        Telegram telegram = new Telegram(0, sender, otherReceiver, message, additionalInfo);

        //if there is no delay, route telegram immediately                       
        if (delay <= 0.0)
            //send the telegram to the recipient
            Discharge(ref receiver, ref telegram);
        //else calculate the time when the telegram should be dispatched
        else
        {
            double CurrentTime = Clock.Current();
            telegram.DispatchTime = CurrentTime + delay;
            //and put it in the queue
            priorityQueue.Add(telegram);
        }
    }

    /// <summary>
    /// This function dispatches any telegrams with a timestamp that has
    /// expired. Any dispatched telegrams are removed from the queue as it
    /// sends out any delayed messages. This method is called each time through   
    /// the main game loop.
    /// </summary>
    public static void DispatchDelayedMessages()
    {
        double CurrentTime = Clock.Current();

        //now peek at the queue to see if any telegrams need dispatching.
        //remove all telegrams from the front of the queue that have gone
        //past their sell by date
        while (!(priorityQueue.Count == 0) &&
                (priorityQueue.ElementAt(0).DispatchTime < CurrentTime) &&
                (priorityQueue.ElementAt(0).DispatchTime > 0))
        {
            //read the telegram from the front of the queue
            Telegram telegram = priorityQueue.ElementAt(0);

            //find the recipient
            BaseEntityInfo receiver = EntityMgr.entityManager.GetEntityFromID(telegram.Receiver);

            //send the telegram to the recipient
            Discharge(ref receiver, ref telegram);

            //remove it from the queue
            priorityQueue.Remove(priorityQueue.ElementAt(0));
        }
    }
}

3 个答案:

答案 0 :(得分:1)

首先,why use struct in this case? C#中的结构与它们在C ++中的结构相同 - 在这种情况下,每次在方法中传递时,对象都会被复制,这可能是一个非常记忆消费解决方案。

其次,尝试使用Generic方法,如下所示:

public Telegram<T>(double time, int otherSender, int otherReceiver,
            Message otherMessage, T info = null)
where T : class
{
    dispatchTime = time;
    sender = otherSender;
    receiver = otherReceiver;
    messageToSend = otherMessage;
    // check for T here
}

如果您要澄清您想要达到的目标,社区可以更好地帮助您。

泛型方法的用法就像是C ++中的模板方法。唯一的问题是如果你想要一个Parametrized构造函数,你必须声明一个generic class,如下所示:

public class Telegram<T>
{
    T additionalInfo;

    Telegram(double time, int otherSender, int otherReceiver,
            Message otherMessage, T info = null)
    where T : class

答案 1 :(得分:0)

你可以使用Convert.ChangeType(object value, Type type) see MSDN但是,就像评论所说的那样,你滥用了一个结构,因为你的对象不是一成不变的,小的,或逻辑上代表一个值,我建议考虑一个class,可能还有通用class

public class Telegram
{
   public Telegram(double time, int otherSender, int otherReceiver,
            Message otherMessage, object info = null)
   {
        DispatchTime = time;
        Sender = otherSender;
        Receiver = otherReceiver;
        MessageToSend = otherMessage;
        ExtraInfo = info;
   }

   public int Reciever {get; private set;}
   public Message MessageToSend {get; private set;}
   public double DispatchTime {get; set;}
   public object ExtraInfo {get; private set;}
}

或作为通用:

public class Telegram<T>
{
   public Telegram(double time, int otherSender, int otherReceiver,
            Message otherMessage, T info = default(T))
   {
        DispatchTime = time;
        Sender = otherSender;
        Receiver = otherReceiver;
        MessageToSend = otherMessage;
        ExtraInfo = info;
   }

   public int Reciever {get; private set;}
   public Message MessageToSend {get; private set;}
   public double DispatchTime {get; set;}
   public T ExtraInfo {get; private set;}
}

泛型类型参数的优点是您可以轻松地知道类型(typeof(T)),并且可以转换为该类型而不必依赖Convert.ChangeTypeT valueAsT = (T)value)并且你可以在T上施加约束,如where T: class, new()表示它必须是一个引用类型,它必须有一个默认构造函数,或者你可以要求一个接口实现,让你可以访问它的成员。 / p>

要详细说明,通用只会公开有关该成员的类型信息,因此不需要将object强制转换为int,而是int

例如:

var myTelegram = new Telegram(1235.3423, 42, 69, myMessageObj, 32);
var extraInfo = myTelegram.ExtraInfo;
var extraInfoType = extraInfo.GetType();
    //extraInfoType is int, and you don't have to unbox it.

有关泛型的更多详细信息,一个很好的起点是http://www.dotnetperls.com/generic - 然后您可以深入了解MSDN或其他资源。

<强>更新

至于您刚才添加澄清的内容,您仍然可以使用泛型,但是您必须对其设置一些类型限制,或者您的SortedSet<T>必须是Telegram<object>,这会限制一些事情。但无论你最终做什么,有用的信息是你也可以在方法级别使用泛型,所以你不必总是将类声明为泛型,例如:

public sealed class MessageDispatcher
{
    public static void DispatchMessage<T>(double delay, int sender,
                            int otherReceiver, Message message,
                            T additionalInfo = default(T))
   ...
}

我只是确定,复制您发布的所有代码并制作了带泛型的版本,因此可以完成,但是使用泛型,您还必须覆盖GetHashCode()方法和{{1}方法(继承自Equals(obj other))以便让它编译,因为你重载了运算符。

但是,如果您不能将类型限制为类似System.Object之类的内容或某些其他界面,您可以从中了解有关该类型的某些属性,那么您可以选择对象,然后使用IComparable<T>广告投放或value as Type

Convert.ChangeType(Object obj, Type type)

答案 2 :(得分:0)

我建议使用Convert.ChangeType选项。在这种情况下使用泛型不会带来很多好处,可能会让你回到MessageDispatcher类的一个角落,因为当你创建SortedSet<Telegram>时 你必须指定SortedSet<Telegram<T>>,但是T可以是任何东西,如果我没记错的话(纠正我,如果我错了)拥有一组不同的Ts不会工作。

每个处理程序都需要知道&#34;类型&#34;额外的信息和如何处理数据,所以传递一个对象或基类就足够了。调度员并不关心额外信息的类型或接收者是否可以实际处理额外信息。当接收者处理一个Telegram时,它可以简单地在额外信息上调用GetType,将其传递给ChangeType方法,如果它成功转换,那么很好......如果没有那么处理转换错误。< / p>

编辑1:您可以简单地执行MyRealType result = extraInfo as MyRealType,然后如果result为NULL,那么您就知道错误类型的额外信息已经过去了。