2015-02-08 138 views
1

我想要做的就是为我的游戏我开发我有一个使用电报结构消息系统如下:是否可以使用类型变量来转换对象的变量?

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; 
    } 
} 

我希望能够做的是,因为额外信息是基于消息类型传递的,并且需要是对象,为了方便不必使用各种额外的信息类型对一堆函数进行编码,我希望获取其装入的对象的类型函数通过额外的信息变量。

我知道我可以用.getType()做到这一点,并将其存储在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)); 
     } 
    } 
} 
+5

你能解释一下为什么你认为'struct'在这里是个好主意吗? – spender 2015-02-08 18:11:52

+3

[“类和结构之间进行选择”](https://msdn.microsoft.com/en-us/library/ms229017%28v=vs.110%29.aspx)表示如下:避免定义一个结构,除非该类型具有以下所有特征:(a)它逻辑上表示一个单一的值,类似于原始类型(int,double等)。 (b)它具有小于16字节的实例大小。 (c)它是不可变的。 (d)不必频繁地装箱。 *根据我的估算,你在四项计数中都失败了。* – spender 2015-02-08 18:15:11

+0

因为我不需要电报作为课程。 – 2015-02-08 18:16:00

回答

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++中的模板方法。唯一的问题是,如果你想有一个参数化的构造函数,你必须声明generic class,像这样:

public class Telegram<T> 
{ 
    T additionalInfo; 

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

不知道,因为我在C++中教过的结构,一直在试图自学C#。也不会有一个通用的方法要求我每次我想发送不同类型的信息时创建一个方法? – 2015-02-08 18:35:25

+0

@ThomasMorse更新了答案。 – VMAtm 2015-02-08 19:09:58

+0

@ThomasMorse不,不需要其他方法 - 对于特定类型的其他信息可能只是一个新的逻辑。 – VMAtm 2015-02-08 19:16:45

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)),你可以投的类型,而不必依赖你可以对T施加约束,如where T: class, new(),它说它必须是一个引用类型,它必须有一个默认的构造函数,或者你可以要求一个接口实现,让你访问它的成员。

要详细说明,一般只会公开关于成员的类型信息,所以不需要将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()方法和Equals(obj other)方法(从System.Object继承)你超载了运营商。

不过,如果你不能限制说类似IComparable<T>或其他一些界面,你会从寂寂类型的一些属性中受益,那么你不妨用对象去,然后用value as Type类型铸造或Convert.ChangeType(Object obj, Type type)

public class Telegram<T> 
{ 
    public int Sender { get; private set; } 
    public int Receiver { get; private set; } 

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

    //for delayed messages 
    public double DispatchTime { get; set; } 

    //for any additional info 
    public T ExtraInfo { get; private set; } 

    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 override int GetHashCode() 
    { 
     return base.GetHashCode(); 
    } 

    public override bool Equals(object obj) 
    { 
     return base.Equals(obj); 
    } 

    /*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<T> t1, Telegram<T> 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<T> t1, Telegram<T> 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<T> t1, Telegram<T> t2) 
    { 
     if (t1 == t2) 
      return false; 
     else 
      return (t1.DispatchTime < t2.DispatchTime); 
    } 

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

public 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<object>> priorityQueue = new SortedSet<Telegram<object>>(); 

    /// <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<T>(BaseEntityInfo receiver, Telegram<T> message) 
    { 
     if (!receiver.HandleMessage(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> 
    public static void DispatchMessage<T>(double delay, int sender, 
          int otherReceiver, Message message, 
          T additionalInfo = default(T)) 
    { 
     //get the reciever 
     BaseEntityInfo receiver = EntityMgr.entityManager.GetEntityFromID(otherReceiver); 

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

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

     //if there is no delay, route telegram immediately      
     if (delay <= 0.0) 
      //send the telegram to the recipient 
      Discharge(receiver, 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 
      var telegram = priorityQueue.ElementAt(0); 

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

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

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

使用通用类型的问题是,当我只需要一种类型的电报时,我会收到大量的电报副本,并且需要为每种额外类型实现一个电报类我想要发送的信息可能在一百个左右。 – 2015-02-08 18:33:16

+0

你不需要实现多个实例,泛型在C#中是真正的泛型,与Java不同,一类'Telegram '可以在任何地方使用,无论'T'是什么类型。即使在运行时。 – tophallen 2015-02-08 18:34:27

+0

好的,我不知道。那么我怎么称呼那个功能呢?不熟悉泛型。 – 2015-02-08 18:38:52

0

我会建议使用Convert.ChangeType选项。在这种情况下,使用仿制药不会增加多少好处,并因为当你创建SortedSet<Telegram> 你就必须指定SortedSet<Telegram<T>>可以背你到一个角落里的MessageDispatcher类中,而T3可以是任何东西,如果我,如果我记得没错(指正错了)拥有不同的Ts的集合将不起作用。

每个处理器需要知道的额外的信息,以及如何处理数据的“类型”,围绕一个对象或基类,所以路过就足够了。调度员不关心额外信息的类型或接收者是否可以实际处理额外的信息。当接收者处理一个电报时,它可以简单地在额外的信息上调用GetType,将它传递给ChangeType方法,如果它成功转换,那么很好......如果不是,那么处理这个转换错误。

编辑1:你可以简单地做一个MyRealType result = extraInfo as MyRealType,然后如果result是NULL,那么你知道错误类型的额外信息已经过去的接收器。

相关问题