我在基礎知識上絞盡腦汁。
我有幾個(例如,假設)共享一個公共基類的訊息類。我有一個接受基類作為引數的介面。到目前為止,我認為很容易使用方法多載來處理單獨方法中的特定型別的訊息。
您如何才能運行以下示例:
using System;
namespace MethodOverloading
{
// for the example: we are sending messages
// which have a common base class
public class MessageBase
{
public readonly string Value;
public MessageBase() { Value = GetType().Name; }
}
// and there are a couple of concrete instances
public class Message1000 : MessageBase { }
public class Message2000 : MessageBase { }
public class Message3000 : MessageBase { }
public class Message4000 : MessageBase { }
public class Message5000 : MessageBase { }
// and of cource we have an interface receiving all messages but only with one method for the base defined
public interface IHandler
{
void ReceiveMessage(MessageBase msg);
}
// the handlers should do some method overloading so a overloaded method can be implemented for each supported message
// and the base message catches all unsupported messages (e.g. log: ey, missed an overload for this type)
// Handler 1 tries to overload the interface method
public class Handler1 : IHandler
{
public void ReceiveMessage(MessageBase msg) { Console.WriteLine($"Handler1.ReceiveMessage(MessageBase:{msg.Value})"); }
public void ReceiveMessage(Message1000 msg) { Console.WriteLine($"Handler1.ReceiveMessage(Message1000:{msg.Value})"); }
public void ReceiveMessage(Message2000 msg) { Console.WriteLine($"Handler1.ReceiveMessage(Message2000:{msg.Value})"); }
public void ReceiveMessage(Message3000 msg) { Console.WriteLine($"Handler1.ReceiveMessage(Message3000:{msg.Value})"); }
public void ReceiveMessage(Message4000 msg) { Console.WriteLine($"Handler1.ReceiveMessage(Message4000:{msg.Value})"); }
// intentionally no overload for Message5000
}
// Handler 2 provides one interface method and has protected overloads...
public class Handler2 : IHandler
{
public void ReceiveMessage(MessageBase msg)
{
Console.Write($"Handler2.ReceiveMessage(MessageBase:{msg.Value}) > ");
HandleMessage(msg);
}
protected void HandleMessage(MessageBase msg) { Console.WriteLine($"Handler2.HandleMessage(MessageBase:{msg.Value})"); }
protected void HandleMessage(Message1000 msg) { Console.WriteLine($"Handler2.HandleMessage(Message1000:{msg.Value})"); }
protected void HandleMessage(Message2000 msg) { Console.WriteLine($"Handler2.HandleMessage(Message2000:{msg.Value})"); }
protected void HandleMessage(Message3000 msg) { Console.WriteLine($"Handler2.HandleMessage(Message3000:{msg.Value})"); }
protected void HandleMessage(Message4000 msg) { Console.WriteLine($"Handler2.HandleMessage(Message4000:{msg.Value})"); }
// intentionally no overload for Message5000
}
class Program
{
static void Main(string[] args)
{
// so lets give it a try ....
Console.WriteLine("Testing method overloads");
MessageBase msgBase = new MessageBase();
Message1000 msg1000 = new Message1000();
Message2000 msg2000 = new Message2000();
Message3000 msg3000 = new Message3000();
Message4000 msg4000 = new Message4000();
Message5000 msg5000 = new Message5000();
Console.WriteLine("Handler1:");
Handler1 handler1 = new Handler1();
handler1.ReceiveMessage(msgBase);
handler1.ReceiveMessage(msg1000);
handler1.ReceiveMessage(msg2000);
handler1.ReceiveMessage(msg3000);
handler1.ReceiveMessage(msg4000);
handler1.ReceiveMessage(msg5000);
Console.WriteLine("iHandler1:");
IHandler ihandler1 = new Handler1();
ihandler1.ReceiveMessage(msgBase);
ihandler1.ReceiveMessage(msg1000);
ihandler1.ReceiveMessage(msg2000);
ihandler1.ReceiveMessage(msg3000);
ihandler1.ReceiveMessage(msg4000);
ihandler1.ReceiveMessage(msg5000);
Console.WriteLine("Handler2:");
Handler2 handler2 = new Handler2();
handler2.ReceiveMessage(msgBase);
handler2.ReceiveMessage(msg1000);
handler2.ReceiveMessage(msg2000);
handler2.ReceiveMessage(msg3000);
handler2.ReceiveMessage(msg4000);
handler2.ReceiveMessage(msg5000);
Console.WriteLine("iHandler2:");
IHandler ihandler2 = new Handler2();
ihandler2.ReceiveMessage(msgBase);
ihandler2.ReceiveMessage(msg1000);
ihandler2.ReceiveMessage(msg2000);
ihandler2.ReceiveMessage(msg3000);
ihandler2.ReceiveMessage(msg4000);
ihandler2.ReceiveMessage(msg5000);
Console.WriteLine("press any key to exit");
Console.ReadLine();
}
}
}
輸出實際上是:
Testing method overloads
Handler1:
Handler1.ReceiveMessage(MessageBase:MessageBase)
Handler1.ReceiveMessage(Message1000:Message1000)
Handler1.ReceiveMessage(Message2000:Message2000)
Handler1.ReceiveMessage(Message3000:Message3000)
Handler1.ReceiveMessage(Message4000:Message4000)
Handler1.ReceiveMessage(MessageBase:Message5000)
iHandler1:
Handler1.ReceiveMessage(MessageBase:MessageBase)
Handler1.ReceiveMessage(MessageBase:Message1000)
Handler1.ReceiveMessage(MessageBase:Message2000)
Handler1.ReceiveMessage(MessageBase:Message3000)
Handler1.ReceiveMessage(MessageBase:Message4000)
Handler1.ReceiveMessage(MessageBase:Message5000)
Handler2:
Handler2.ReceiveMessage(MessageBase:MessageBase) > Handler2.HandleMessage(MessageBase:MessageBase)
Handler2.ReceiveMessage(MessageBase:Message1000) > Handler2.HandleMessage(MessageBase:Message1000)
Handler2.ReceiveMessage(MessageBase:Message2000) > Handler2.HandleMessage(MessageBase:Message2000)
Handler2.ReceiveMessage(MessageBase:Message3000) > Handler2.HandleMessage(MessageBase:Message3000)
Handler2.ReceiveMessage(MessageBase:Message4000) > Handler2.HandleMessage(MessageBase:Message4000)
Handler2.ReceiveMessage(MessageBase:Message5000) > Handler2.HandleMessage(MessageBase:Message5000)
iHandler2:
Handler2.ReceiveMessage(MessageBase:MessageBase) > Handler2.HandleMessage(MessageBase:MessageBase)
Handler2.ReceiveMessage(MessageBase:Message1000) > Handler2.HandleMessage(MessageBase:Message1000)
Handler2.ReceiveMessage(MessageBase:Message2000) > Handler2.HandleMessage(MessageBase:Message2000)
Handler2.ReceiveMessage(MessageBase:Message3000) > Handler2.HandleMessage(MessageBase:Message3000)
Handler2.ReceiveMessage(MessageBase:Message4000) > Handler2.HandleMessage(MessageBase:Message4000)
Handler2.ReceiveMessage(MessageBase:Message5000) > Handler2.HandleMessage(MessageBase:Message5000)
press any key to exit
處理程式 1 在直接呼叫時實際上可以作業。不幸的是,當作為介面呼叫時,它不是開箱即用的。
盡管我認為至少具有受保護多載的 Handler2 可以解決問題....
我實際上試圖擺脫的是 Handler3 中帶有強制轉換的 switch 陳述句(因為很容易錯過額外的步驟,并且在開發人員無法訪問的基類中執行這種魔法會很棒):
public class Handler3 : IHandler
{
public void ReceiveMessage(MessageBase msg)
{
Console.Write($"Handler3.ReceiveMessage(MessageBase:{msg.Value}) > ");
switch (msg)
{
case Message1000 msg1000: HandleMessage(msg1000); break;
case Message2000 msg2000: HandleMessage(msg2000); break;
case Message3000 msg3000: HandleMessage(msg3000); break;
case Message4000 msg4000: HandleMessage(msg4000); break;
default: Console.WriteLine("dropped because not supported: " msg.Value); break; // for the msg5000
}
}
//protected void HandleMessage(MessageBase msg) { Console.WriteLine($"Handler3.HandleMessage(MessageBase:{msg.Value})"); }
protected void HandleMessage(Message1000 msg) { Console.WriteLine($"Handler3.HandleMessage(Message1000:{msg.Value})"); }
protected void HandleMessage(Message2000 msg) { Console.WriteLine($"Handler3.HandleMessage(Message2000:{msg.Value})"); }
protected void HandleMessage(Message3000 msg) { Console.WriteLine($"Handler3.HandleMessage(Message3000:{msg.Value})"); }
protected void HandleMessage(Message4000 msg) { Console.WriteLine($"Handler3.HandleMessage(Message4000:{msg.Value})"); }
// intentionally no overload for Message5000
}
這實際上會如輸出所示那樣作業:
Handler3:
Handler3.ReceiveMessage(MessageBase:MessageBase) > dropped because not supported: MessageBase
Handler3.ReceiveMessage(MessageBase:Message1000) > Handler3.HandleMessage(Message1000:Message1000)
Handler3.ReceiveMessage(MessageBase:Message2000) > Handler3.HandleMessage(Message2000:Message2000)
Handler3.ReceiveMessage(MessageBase:Message3000) > Handler3.HandleMessage(Message3000:Message3000)
Handler3.ReceiveMessage(MessageBase:Message4000) > Handler3.HandleMessage(Message4000:Message4000)
Handler3.ReceiveMessage(MessageBase:Message5000) > dropped because not supported: Message5000
但是,如果您錯過了使用該變體的方法多載,編譯器會抱怨并阻止構建,這是一個好處。
uj5u.com熱心網友回復:
嗯,是。請記住,多載決議是在編譯時使用變數的編譯時型別完成的,而不是在運行時完成。運行時型別無關緊要。
IHandler只有過載void ReceiveMessage(MessageBase msg)。因此,當您呼叫 時IHandler.ReceiveMessage(msg),無論子類msg碰巧是什么,它都必須呼叫IHandler.ReceiveMessage(MessageBase msg),因為這是唯一IHandler定義的方法。
Handler1定義其他不在 中的其他方法并不重要IHandler:您的Main方法正在使用 的實體IHandler,因此void ReceiveMessage(MessageBase msg)是它可以看到的唯一多載。
在 中Handler2.ReceiveMessage(MessageBase msg),msg具有編譯時型別MessageBase。您可以在方法簽名中看到它。所以當你呼叫時HandleMessage(msg),msg是 a MessageBase,所以編譯器必須選擇HandleMessage(MessageBase msg)多載。
實作您所追求的一種可能方法是使用訪問者模式。這讓您可以使用編譯時型別為 的變數MessageBase,并通過要求它呼叫您的特定方法來找出它的運行時型別。就像是:
public interface IMessageVisitor
{
void Accept(Message1000 msg);
void Accept(Message2000 msg);
}
// for the example: we are sending messages
// which have a common base class
public abstract class MessageBase
{
public readonly string Value;
public MessageBase() { Value = GetType().Name; }
public abstract void Visit(IMessageVisitor visitor);
}
// and there are a couple of concrete instances
public class Message1000 : MessageBase
{
public override void Visit(IMessageVisitor visitor) => visitor.Accept(this);
}
public class Message2000 : MessageBase
{
public override void Visit(IMessageVisitor visitor) => visitor.Accept(this);
}
public interface IHandler
{
void ReceiveMessage(MessageBase msg);
}
public class Handler1 : IHandler, IMessageVisitor
{
public void ReceiveMessage(MessageBase msg) => msg.Visit(this);
public void Accept(Message1000 msg) => Console.WriteLine("Message1000");
public void Accept(Message2000 msg) => Console.WriteLine("Message2000");
}
在dotnetfiddle.net上查看。
uj5u.com熱心網友回復:
方法是根據物件的型別而不是引數的型別來決議的。即您將呼叫Handler1.ReceiveMessage或Handler2.ReceiveMessage基于處理程式物件的型別,而不是訊息物件。這在技術上稱為“單一調度”
您想要的是“多次分派”,即您希望根據兩個不同的物件來決議該方法。
一種方法是將介面更改為抽象基類并使用模式匹配將型別映射到正確的方法
public abstract class HandlerBase
{
protected abstract void ReceiveMessage(Message1000 msg);
protected abstract void ReceiveMessage(Message2000 msg);
// etc
void ReceiveMessage(MessageBase msg){
switch(msg){
case Message1000 msg1000:
ReceiveMessage(msg1000);
break;
case Message2000 msg2000:
ReceiveMessage(msg2000);
break;
// etc
}
}
}
另一種選擇是訪問者模式:
public abstract class MessageBase
{
public readonly string Value;
public MessageBase() { Value = GetType().Name; }
public abstract void Visit(IVisitor visitor);
}
public class Message1000
{
public override void Visit(IVisitor visitor) => visitor.ReceiveMessage(this);
}
public interface IVisitor{
void ReceiveMessage(Message1000 msg);
void ReceiveMessage(Message2000 msg);
// etc...
}
訪問者模式將強制您實作所有需要的方法,新的訊息型別必須有一個 Accept 方法,要實作這一點,您需要一個新的 ReceiveMessage 多載,并且必須由所有訪問者/處理程式實作。這是否有益取決于您。
第三種選擇是使用"dynamic",但我不推薦它,因為它會禁用所有型別檢查。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/389971.html
上一篇:C#獲取所有可能的組合
下一篇:使用webapi更新多條記錄
