- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
在我的 IRC 应用程序中,应用程序从 IRC 服务器接收内容。内容被发送到工厂,工厂吐出一个 IMessage
应用程序的表示层可以使用的对象。 IMessage
接口(interface)和单个实现如下所示。
public interface IMessage
{
object GetContent();
}
public interface IMessage<out TContent> : IMessage where TContent : class
{
TContent Content { get; }
}
public class ServerMessage : IMessage<string>
{
public ServerMessage(string content)
{
this.Content = content;
}
public string Content { get; private set; }
public object GetContent()
{
return this.Content;
}
}
接收IMessage
对象,表示层订阅在我的域层中发布的通知。通知系统迭代一组订阅者到指定的IMessage
。实现并向订阅者触发回调方法。
public interface ISubscription
{
void Unsubscribe();
}
public interface INotification<TMessageType> : ISubscription where TMessageType : class, IMessage
{
void Register(Action<TMessageType, ISubscription> callback);
void ProcessMessage(TMessageType message);
}
internal class Notification<TMessage> : INotification<TMessage> where TMessage : class, IMessage
{
private Action<TMessage, ISubscription> callback;
public void Register(Action<TMessage, ISubscription> callbackMethod)
{
this.callback = callbackMethod;
}
public void Unsubscribe()
{
this.callback = null;
}
public void ProcessMessage(TMessage message)
{
this.callback(message, this);
}
}
public class NotificationManager
{
private ConcurrentDictionary<Type, List<ISubscription>> listeners =
new ConcurrentDictionary<Type, List<ISubscription>>();
public ISubscription Subscribe<TMessageType>(Action<TMessageType, ISubscription> callback) where TMessageType : class, IMessage
{
Type messageType = typeof(TMessageType);
// Create our key if it doesn't exist along with an empty collection as the value.
if (!listeners.ContainsKey(messageType))
{
listeners.TryAdd(messageType, new List<ISubscription>());
}
// Add our notification to our listener collection so we can publish to it later, then return it.
var handler = new Notification<TMessageType>();
handler.Register(callback);
List<ISubscription> subscribers = listeners[messageType];
lock (subscribers)
{
subscribers.Add(handler);
}
return handler;
}
public void Publish<T>(T message) where T : class, IMessage
{
Type messageType = message.GetType();
if (!listeners.ContainsKey(messageType))
{
return;
}
// Exception is thrown here due to variance issues.
foreach (INotification<T> handler in listeners[messageType])
{
handler.ProcessMessage(message);
}
}
}
为了演示上面的代码是如何工作的,我有一个简单的控制台应用程序,它订阅来自上面 ServerMessage
的通知。类型。控制台应用程序首先通过传递 ServerMessage
来发布对象进入 Publish<T>
方法直接。这没有任何问题。
第二个示例让应用程序使用工厂方法创建一个 IMessage 实例。然后将 IMessage 实例传递给 Publish<T>
方法,导致我的方差问题抛出 InvalidCastException
.
class Program
{
static void Main(string[] args)
{
var notificationManager = new NotificationManager();
ISubscription subscription = notificationManager.Subscribe<ServerMessage>(
(message, sub) => Console.WriteLine(message.Content));
notificationManager.Publish(new ServerMessage("This works"));
IMessage newMessage = MessageFactoryMethod("This throws exception");
notificationManager.Publish(newMessage);
Console.ReadKey();
}
private static IMessage MessageFactoryMethod(string content)
{
return new ServerMessage(content);
}
}
异常表明我无法转换 INotification<IMessage>
(Publish 方法是什么理解正在发布的消息是)到 INotification<ServerMessage>
.
我试图将 INotification 接口(interface)通用标记为逆变,例如 INotification<in TMessageType>
但不能那样做,因为我正在消费 TMessageType
作为 Register
的参数方法的回调。我应该将接口(interface)分成两个单独的接口(interface)吗?一种可以注册,一种可以消费?这是最好的选择吗?
任何额外的帮助都会很棒。
最佳答案
这里的基本问题是您正在尝试以变体方式使用您的类型,但您尝试使用的语法不支持这种方式。感谢您更新且现在完整(并且几乎是最小的)代码示例,很明显您根本无法按照现在编写的方式执行此操作。
有问题的接口(interface),特别是您要使用的方法(即 ProcessMessage()
,实际上可以声明为协变接口(interface)(如果您将 Register()
方法拆分为单独的界面)。但这样做并不能解决您的问题。
你看,问题是你正在尝试分配 INotification<ServerMessage>
的实现到类型为 INotification<IMessage>
的变量.请注意,一旦将该实现分配给该类型的变量,调用者就可以传递 IMessage
的任何实例。方法,即使不是 ServerMessage
的实例.但是实际的实现期望(不,要求!)一个 ServerMessage
的实例。 .
换句话说,您尝试编写的代码根本不是静态安全的。它无法在编译时保证类型匹配,而这不是 C# 愿意做的事情。
一种选择是通过使其成为非泛型来削弱接口(interface)的类型安全性。 IE。让它总是接受 IMessage
实例。然后每个实现都必须根据其需要进行转换。编码错误只会在运行时被捕获,带有 InvalidCastException
,但正确的代码可以正常运行。
另一种选择是设置情况,以便知道完整的类型参数。例如,制作 PushMessage()
通用方法,以便它可以调用 Publish()
使用 ServerMessage
的类型参数而不是 IMessage
:
private void OnMessageProcessed(IrcMessage message, IrcCommand command, ICommandFormatter response)
{
this.OnMessageProcessed(message);
ServerMessage formattedMessage = (ServerMessage)response.FormatMessage(message, command);
this.PushMessage(formattedMessage);
}
private void PushMessage<T>(T notification) where T : IMessage
{
this.notificationManager.Publish(notification);
}
这样,类型参数T
将在 foreach
中完全匹配在遇到问题的地方循环。
就个人而言,我更喜欢第二种方法。我意识到在您当前的实现中,这是行不通的。但恕我直言,可能值得重新审视更广泛的设计,看看您是否可以在始终保留泛型类型的同时完成相同的功能,以便它可用于确保编译时类型安全。
关于c# - foreach 循环无法将类型转换为它实现的接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28575411/
我正在尝试编写一个相当多态的库。我遇到了一种更容易表现出来却很难说出来的情况。它看起来有点像这样: {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE
谁能解释一下这个表达式是如何工作的? type = type || 'any'; 这是否意味着如果类型未定义则使用“任意”? 最佳答案 如果 type 为“falsy”(即 false,或 undef
我有一个界面,在IAnimal.fs中, namespace Kingdom type IAnimal = abstract member Eat : Food -> unit 以及另一个成功
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: What is the difference between (type)value and type(va
在 C# 中,default(Nullable) 之间有区别吗? (或 default(long?) )和 default(long) ? Long只是一个例子,它可以是任何其他struct类型。 最
假设我有一个案例类: case class Foo(num: Int, str: String, bool: Boolean) 现在我还有一个简单的包装器: sealed trait Wrapper[
这个问题在这里已经有了答案: Create C# delegate type with ref parameter at runtime (1 个回答) 关闭 2 年前。 为了即时创建委托(dele
我正在尝试获取图像的 dct。一开始我遇到了错误 The function/feature is not implemented (Odd-size DCT's are not implemented
我正在尝试使用 AFNetworking 的 AFPropertyListRequestOperation,但是当我尝试下载它时,出现错误 预期的内容类型{( “应用程序/x-plist” )}, 得
我在下面收到错误。我知道这段代码的意思,但我不知道界面应该是什么样子: Element implicitly has an 'any' type because index expression is
我尝试将 SignalType 从 ReactiveCocoa 扩展为自定义 ErrorType,代码如下所示 enum MyError: ErrorType { // .. cases }
我无法在任何其他问题中找到答案。假设我有一个抽象父类(super class) Abstract0,它有两个子类 Concrete1 和 Concrete1。我希望能够在 Abstract0 中定义类
我想知道为什么这个索引没有用在 RANGE 类型中,而是用在 INDEX 中: 索引: CREATE INDEX myindex ON orders(order_date); 查询: EXPLAIN
我正在使用 RxJava,现在我尝试通过提供 lambda 来订阅可观察对象: observableProvider.stringForKey(CURRENT_DELETED_ID) .sub
我已经尝试了几乎所有解决问题的方法,其中包括。为 提供类型使用app.use(express.static('public'))还有更多,但我似乎无法为此找到解决方案。 index.js : imp
以下哪个 CSS 选择器更快? input[type="submit"] { /* styles */ } 或 [type="submit"] { /* styles */ } 只是好
我不知道这个设置有什么问题,我在 IDEA 中获得了所有注释(@Controller、@Repository、@Service),它在行号左侧显示 bean,然后转到该 bean。 这是错误: 14-
我听从了建议 registering java function as a callback in C function并且可以使用“简单”类型(例如整数和字符串)进行回调,例如: jstring j
有一些 java 类,加载到 Oracle 数据库(版本 11g)和 pl/sql 函数包装器: create or replace function getDataFromJava( in_uLis
我已经从 David Walsh 的 css 动画回调中获取代码并将其修改为 TypeScript。但是,我收到一个错误,我不知道为什么: interface IBrowserPrefix { [
我是一名优秀的程序员,十分优秀!