插件式框架开发的优势劣势

lccleo 2016-06-21 10:03:57
插件式开发的优势不用说了,楼主有个工业上位机的小项目基本做得差不多了,和下位机用串口和以太网通信。
现在想把两种通信做成 主程序的插件(看起来高端 也有其他好处)
但是众所周知,插件是根据主程序的接口来实现的,我想问下这样可移植性不就低了吗? 假如别的部门需要我的串口通信插件,他们的主程序得弄个跟我插件一样的接口先,对吗?

另外我们后面想用第三方控件重做UI,UI层能用插件来优化更新吗?就是插件改变UI的意思

插件也是继承接口的类库编成dll文件给别人用,那直接写一个类变成dll文件,别人用里面的方法,这个和插件式框架对比如何?
谢谢解答



...全文
806 1 收藏 27
写回复
27 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
kavilee 2017-01-12
引用 22 楼 sp1234 的回复:
[quote=引用 楼主 lccleo 的回复:] 但是众所周知,插件是根据主程序的接口来实现的,我想问下这样可移植性不就低了吗?
主程序只是引导。它引导进来第一个插件,这个插件就随后动态带了无数种新的可能性。[/quote]
引用 22 楼 sp1234 的回复:
[quote=引用 楼主 lccleo 的回复:] 但是众所周知,插件是根据主程序的接口来实现的,我想问下这样可移植性不就低了吗?
主程序只是引导。它引导进来第一个插件,这个插件就随后动态带了无数种新的可能性。[/quote] 你好,请问MEF是不支持动态加载和卸载插件的,你是通过什么原理实现动态的呢。我知道MAF是可以通过AppDomain实现动态加载
回复
lccleo 2016-07-06
引用 20 楼 sp1234 的回复:
接下来提供2个命令实例。
using System.ComponentModel.Composition;

namespace ConsoleApplication1
{
    [Export(typeof(ICommand))]
    [ExportMetadata("CommandName", "hello")]
    public class Class1 : ICommand
    {
        public string Execute(string input)
        {
            return "你好,您输入了 " + input.Length + "个字符参数。";
        }
    }
}
using System.ComponentModel.Composition;
using System.Data;

namespace ConsoleApplication1
{
    [Export(typeof(ICommand))]
    [ExportMetadata("CommandName", "计算")]
    public class Class2 : ICommand
    {
        public string Execute(string input)
        {
            return new DataTable().Compute(input, null).ToString();
        }
    }
}
并且,以为我前边的例子把 Program 类写成 private 类了(无法从外边共享访问 ImportObject 方法),而且csdn也不能提交修改,那么就请把这段 Class3写到 Program 类中,也就是 Program 最后结束的大括号前边(这样就能访问到 private class Program 类)。

        [Export(typeof(ICommand))]
        [ExportMetadata("CommandName", "load")]
        class Class3 : ICommand
        {
            public string Execute(string input)
            {
                Program.db = Program.ImportObject<CommandDB>(input);
                return "从" + input + "加载了新的插件";
            }
        }
现在再执行手动测试,可以输入命令“hello world”,或者“计算 1+2”等等,可以看到你自己没写任何反射代码,ImportObject 方法(也就是 MEF框架)帮你找到了所有命令,装配起来。 接下来才是证明 MEF 框架的功能的时刻!你现在可以创建另外一个工程 ConsoleApplication2,引用 MEF 类库,并且让它引用 ConsoleApplication1 工程,增加一个测试命令
using ConsoleApplication1;
using System.ComponentModel.Composition;
using System.Net;

namespace ConsoleApplication2
{
    [Export(typeof(ICommand))]
    [ExportMetadata("CommandName", "IP")]
    public class Class1 : ICommand
    {
        public string Execute(string input)
        {
            return new WebClient().DownloadString("http://1212.ip138.com/ic.asp");
        }
    }
}
注意,现在要编译生成 ConsoleApplication2,然后记下其生成的 exe 地址。例如看到其exe文件生成到这个目录下 D:\ConsoleApplication1\ConsoleApplication2\bin\Debug,复制这个路径到键盘缓冲区。 下载重新运行 ConsoleApplciation1,打入命令“IP",你会发现此时显示“找不到命令处理程序”。但是你可以打入命令“load [Ctrl+]V键”,也就是拷贝键盘缓冲区的内容成为“load D:\ConsoleApplication1\ConsoleApplication2\bin\Debug”,之后你在执行命令"IP",你会发现ConsoleApplication2中的class1实例执行了。 此时可以设置断点,你会发现vs调试器可以跨程序去调试 MEF 所加载的exe。因为 MEF 将另一个目录下的exe、dll等等都加载到当前环境中了。 如果你打入命令“load .”,然后你会发现"IP“命令又不能执行了。如果 load 那个 ConsoleApplication2的exe目录,则命令 IP 又可以执行了。 这说明,在你没有停机的情况下,你其实可以切换不同目录下保存的插件。MEF的作用,就是可以动态地从任意目录下加载你想查找的插件,而不需要自己手写反射、加载等等代码,也不会遇到许多困难的 bug 问题。 接下来,你可以把这个非常简单的 ImportObject<T>() 方法扩展一下,改成这样:
public static T HotRepair<T>(string path) where T : new()
{
    var source = new DirectoryInfo(path);
    var target = new DirectoryInfo(Path.GetTempPath());
    if (!target.Exists)
        target.Create();
    foreach (var f in source.EnumerateFiles())
    {
        try
        {
            f.CopyTo(Path.Combine(target.FullName, f.Name));
        }
        catch { }
    }
    return ImportObject<T>(target.FullName);
}
它实际上是把目标目录里边的文件都拷贝到一个“临时文件目录”去加载,这其实就做到了本地插件的“热更新”。你可以随时覆盖、删除插件目录里边的文件,随时从同一个目录重新加载插件的新版本。而不需要像上面那样去到不同的目录里边其加载插件的新版本。 最后,你可以在你的写 asp.net 的某个目录下写两个 ashx文件,一个用来返回 json 格式数组列表,表示网站上某一目录下的所有文件的“文件名、md5”值列表。另一个ashx用来用一条 Response.TransmitFile 语句来方便地下载文件。 然后你可以另写一个远程 HotRepair 方法,让它自动从互联网上读取第一个ashx,得到要下载的文件的列表。然后把本地插件目录里多余的文件删除,在列表中的文件比较一下本地md5,只下载更新那些md5不一样的文件。通过本地跟互联网上的文件的比较,按需随时可以热更新本地插件。 不管是桌面应用,还是 windows service 服务系统,你只要部署一个2k 字节的小文件,剩下的事情就可以由这个小文件(引擎)动态加载。例如一个桌面应用,它加载第一个插件是一个“主界面菜单用户控件”(放在目录1),然后这个菜单控件动态加载菜单项插件(放在目录2),然后菜单项在运行当第一次访问某个大模块的时候才去加载这个大模块的插件目录,而没有访问到的模块其对应的插件目录则不需要下载。
动态加载插件 牛人
回复
sgyiliya 2016-06-29
收藏了,有空仔细研究。
回复
tanta 2016-06-28
插件的优势是可以在主程序不变更的情况下,实现某些功能的扩展或变动,优势还是不少的。
回复
masanaka 2016-06-28
引用 20 楼 sp1234 的回复:
接下来提供2个命令实例。
using System.ComponentModel.Composition;

namespace ConsoleApplication1
{
    [Export(typeof(ICommand))]
    [ExportMetadata("CommandName", "hello")]
    public class Class1 : ICommand
    {
        public string Execute(string input)
        {
            return "你好,您输入了 " + input.Length + "个字符参数。";
        }
    }
}
using System.ComponentModel.Composition;
using System.Data;

namespace ConsoleApplication1
{
    [Export(typeof(ICommand))]
    [ExportMetadata("CommandName", "计算")]
    public class Class2 : ICommand
    {
        public string Execute(string input)
        {
            return new DataTable().Compute(input, null).ToString();
        }
    }
}
并且,以为我前边的例子把 Program 类写成 private 类了(无法从外边共享访问 ImportObject 方法),而且csdn也不能提交修改,那么就请把这段 Class3写到 Program 类中,也就是 Program 最后结束的大括号前边(这样就能访问到 private class Program 类)。

        [Export(typeof(ICommand))]
        [ExportMetadata("CommandName", "load")]
        class Class3 : ICommand
        {
            public string Execute(string input)
            {
                Program.db = Program.ImportObject<CommandDB>(input);
                return "从" + input + "加载了新的插件";
            }
        }
现在再执行手动测试,可以输入命令“hello world”,或者“计算 1+2”等等,可以看到你自己没写任何反射代码,ImportObject 方法(也就是 MEF框架)帮你找到了所有命令,装配起来。 接下来才是证明 MEF 框架的功能的时刻!你现在可以创建另外一个工程 ConsoleApplication2,引用 MEF 类库,并且让它引用 ConsoleApplication1 工程,增加一个测试命令
using ConsoleApplication1;
using System.ComponentModel.Composition;
using System.Net;

namespace ConsoleApplication2
{
    [Export(typeof(ICommand))]
    [ExportMetadata("CommandName", "IP")]
    public class Class1 : ICommand
    {
        public string Execute(string input)
        {
            return new WebClient().DownloadString("http://1212.ip138.com/ic.asp");
        }
    }
}
注意,现在要编译生成 ConsoleApplication2,然后记下其生成的 exe 地址。例如看到其exe文件生成到这个目录下 D:\ConsoleApplication1\ConsoleApplication2\bin\Debug,复制这个路径到键盘缓冲区。 下载重新运行 ConsoleApplciation1,打入命令“IP",你会发现此时显示“找不到命令处理程序”。但是你可以打入命令“load [Ctrl+]V键”,也就是拷贝键盘缓冲区的内容成为“load D:\ConsoleApplication1\ConsoleApplication2\bin\Debug”,之后你在执行命令"IP",你会发现ConsoleApplication2中的class1实例执行了。 此时可以设置断点,你会发现vs调试器可以跨程序去调试 MEF 所加载的exe。因为 MEF 将另一个目录下的exe、dll等等都加载到当前环境中了。 如果你打入命令“load .”,然后你会发现"IP“命令又不能执行了。如果 load 那个 ConsoleApplication2的exe目录,则命令 IP 又可以执行了。 这说明,在你没有停机的情况下,你其实可以切换不同目录下保存的插件。MEF的作用,就是可以动态地从任意目录下加载你想查找的插件,而不需要自己手写反射、加载等等代码,也不会遇到许多困难的 bug 问题。 接下来,你可以把这个非常简单的 ImportObject<T>() 方法扩展一下,改成这样:
public static T HotRepair<T>(string path) where T : new()
{
    var source = new DirectoryInfo(path);
    var target = new DirectoryInfo(Path.GetTempPath());
    if (!target.Exists)
        target.Create();
    foreach (var f in source.EnumerateFiles())
    {
        try
        {
            f.CopyTo(Path.Combine(target.FullName, f.Name));
        }
        catch { }
    }
    return ImportObject<T>(target.FullName);
}
它实际上是把目标目录里边的文件都拷贝到一个“临时文件目录”去加载,这其实就做到了本地插件的“热更新”。你可以随时覆盖、删除插件目录里边的文件,随时从同一个目录重新加载插件的新版本。而不需要像上面那样去到不同的目录里边其加载插件的新版本。 最后,你可以在你的写 asp.net 的某个目录下写两个 ashx文件,一个用来返回 json 格式数组列表,表示网站上某一目录下的所有文件的“文件名、md5”值列表。另一个ashx用来用一条 Response.TransmitFile 语句来方便地下载文件。 然后你可以另写一个远程 HotRepair 方法,让它自动从互联网上读取第一个ashx,得到要下载的文件的列表。然后把本地插件目录里多余的文件删除,在列表中的文件比较一下本地md5,只下载更新那些md5不一样的文件。通过本地跟互联网上的文件的比较,按需随时可以热更新本地插件。 不管是桌面应用,还是 windows service 服务系统,你只要部署一个2k 字节的小文件,剩下的事情就可以由这个小文件(引擎)动态加载。例如一个桌面应用,它加载第一个插件是一个“主界面菜单用户控件”(放在目录1),然后这个菜单控件动态加载菜单项插件(放在目录2),然后菜单项在运行当第一次访问某个大模块的时候才去加载这个大模块的插件目录,而没有访问到的模块其对应的插件目录则不需要下载。
回复
比如说,你开发一个“石油勘探管理”的基本框架,里边的各种书本公式、经验公式,都可以是插件。一个博士开发的插件可以在你的系统上面卖1万块钱,而一个普通工人开发的经验公式可能在你的系统上面就卖2千块钱,而你自己的最初的公式插件则可以免费。
回复
引用 楼主 lccleo 的回复:
但是众所周知,插件是根据主程序的接口来实现的,我想问下这样可移植性不就低了吗?
主程序只是引导。它引导进来第一个插件,这个插件就随后动态带了无数种新的可能性。
回复
sp1234_maJia 2016-06-28
看到上面最简单(demo程序也没有任何正规程序所需要的额外处理代码)的例子,你就明白了,插件并不是指自己的类型实现了别人的接口,然后编译出来的exe、dll就叫做插件。其实插件是一个非常特殊的“动作”,而不仅仅是静态名词儿。把动作做到“刚刚好”,这就达到了插件的目的。而停留在静态概念上,到头来一旦实际操作时还是争论不断。 插件肯定是按照接口编程的,符合静态装配组件的规范。但是其用法,是与静态的组件“相反相成”的。 而且 MEF 足够简单,可以从其它目录加载插件,且支持你在vs上直接调试动态插件(不像某些反射框架造成人失去了vs调试能力)。稳定高效。如果加上你能从互联网上自动(高速地)同步插件目录内容,以及你的一点点即时通讯功能,你就能把所有的应用(包括windows服务)设计成为一个智能的生物。它最初只有2、3k字节,而可以动态加载任何新功能,而不停机。
回复
接下来提供2个命令实例。
using System.ComponentModel.Composition;

namespace ConsoleApplication1
{
    [Export(typeof(ICommand))]
    [ExportMetadata("CommandName", "hello")]
    public class Class1 : ICommand
    {
        public string Execute(string input)
        {
            return "你好,您输入了 " + input.Length + "个字符参数。";
        }
    }
}
using System.ComponentModel.Composition;
using System.Data;

namespace ConsoleApplication1
{
    [Export(typeof(ICommand))]
    [ExportMetadata("CommandName", "计算")]
    public class Class2 : ICommand
    {
        public string Execute(string input)
        {
            return new DataTable().Compute(input, null).ToString();
        }
    }
}
并且,以为我前边的例子把 Program 类写成 private 类了(无法从外边共享访问 ImportObject 方法),而且csdn也不能提交修改,那么就请把这段 Class3写到 Program 类中,也就是 Program 最后结束的大括号前边(这样就能访问到 private class Program 类)。

        [Export(typeof(ICommand))]
        [ExportMetadata("CommandName", "load")]
        class Class3 : ICommand
        {
            public string Execute(string input)
            {
                Program.db = Program.ImportObject<CommandDB>(input);
                return "从" + input + "加载了新的插件";
            }
        }
现在再执行手动测试,可以输入命令“hello world”,或者“计算 1+2”等等,可以看到你自己没写任何反射代码,ImportObject 方法(也就是 MEF框架)帮你找到了所有命令,装配起来。 接下来才是证明 MEF 框架的功能的时刻!你现在可以创建另外一个工程 ConsoleApplication2,引用 MEF 类库,并且让它引用 ConsoleApplication1 工程,增加一个测试命令
using ConsoleApplication1;
using System.ComponentModel.Composition;
using System.Net;

namespace ConsoleApplication2
{
    [Export(typeof(ICommand))]
    [ExportMetadata("CommandName", "IP")]
    public class Class1 : ICommand
    {
        public string Execute(string input)
        {
            return new WebClient().DownloadString("http://1212.ip138.com/ic.asp");
        }
    }
}
注意,现在要编译生成 ConsoleApplication2,然后记下其生成的 exe 地址。例如看到其exe文件生成到这个目录下 D:\ConsoleApplication1\ConsoleApplication2\bin\Debug,复制这个路径到键盘缓冲区。 下载重新运行 ConsoleApplciation1,打入命令“IP",你会发现此时显示“找不到命令处理程序”。但是你可以打入命令“load [Ctrl+]V键”,也就是拷贝键盘缓冲区的内容成为“load D:\ConsoleApplication1\ConsoleApplication2\bin\Debug”,之后你在执行命令"IP",你会发现ConsoleApplication2中的class1实例执行了。 此时可以设置断点,你会发现vs调试器可以跨程序去调试 MEF 所加载的exe。因为 MEF 将另一个目录下的exe、dll等等都加载到当前环境中了。 如果你打入命令“load .”,然后你会发现"IP“命令又不能执行了。如果 load 那个 ConsoleApplication2的exe目录,则命令 IP 又可以执行了。 这说明,在你没有停机的情况下,你其实可以切换不同目录下保存的插件。MEF的作用,就是可以动态地从任意目录下加载你想查找的插件,而不需要自己手写反射、加载等等代码,也不会遇到许多困难的 bug 问题。 接下来,你可以把这个非常简单的 ImportObject<T>() 方法扩展一下,改成这样:
public static T HotRepair<T>(string path) where T : new()
{
    var source = new DirectoryInfo(path);
    var target = new DirectoryInfo(Path.GetTempPath());
    if (!target.Exists)
        target.Create();
    foreach (var f in source.EnumerateFiles())
    {
        try
        {
            f.CopyTo(Path.Combine(target.FullName, f.Name));
        }
        catch { }
    }
    return ImportObject<T>(target.FullName);
}
它实际上是把目标目录里边的文件都拷贝到一个“临时文件目录”去加载,这其实就做到了本地插件的“热更新”。你可以随时覆盖、删除插件目录里边的文件,随时从同一个目录重新加载插件的新版本。而不需要像上面那样去到不同的目录里边其加载插件的新版本。 最后,你可以在你的写 asp.net 的某个目录下写两个 ashx文件,一个用来返回 json 格式数组列表,表示网站上某一目录下的所有文件的“文件名、md5”值列表。另一个ashx用来用一条 Response.TransmitFile 语句来方便地下载文件。 然后你可以另写一个远程 HotRepair 方法,让它自动从互联网上读取第一个ashx,得到要下载的文件的列表。然后把本地插件目录里多余的文件删除,在列表中的文件比较一下本地md5,只下载更新那些md5不一样的文件。通过本地跟互联网上的文件的比较,按需随时可以热更新本地插件。 不管是桌面应用,还是 windows service 服务系统,你只要部署一个2k 字节的小文件,剩下的事情就可以由这个小文件(引擎)动态加载。例如一个桌面应用,它加载第一个插件是一个“主界面菜单用户控件”(放在目录1),然后这个菜单控件动态加载菜单项插件(放在目录2),然后菜单项在运行当第一次访问某个大模块的时候才去加载这个大模块的插件目录,而没有访问到的模块其对应的插件目录则不需要下载。
回复
我给你写一个 MEF 动态更新插件的例子,最终目的是可以让一个程序在“不停机”的情况下,程序自动从互联网上给它自己更新(一堆)插件版本,并且立刻使用起来新的插件(同时废弃旧的插件)。我这里只写最基本的例子,使用最基本的MEF语法。要用来设计自己的框架,那么你可能需要自己去扩展。 MEF 可以针对任何 .net 对象进行反射处理,不管是桌面自定义用户控件、Silverlight 控件,还是你自定义的任何其它对象。不失一般性,我用最简单的“自定义类型”来说明它,并且我一个“傻瓜化的”控制台程序来说明插件的机制,并且用特别简单的“用户输入:命令+参数,然后程序执行”的文本模式说明功能需求,不要再说“一个加减乘除四则运算题有必要用委托吗?”这样的问题啊!这里明显是抛砖引玉的,不是有意Readline()语句来贬低我们的编程经验的啊! 首先我们创建 ConsoleApplication1。
using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                Console.BackgroundColor = ConsoleColor.White;
                Console.WriteLine("请输入命令和参数:");
                var input = Console.ReadLine();
                Console.BackgroundColor = ConsoleColor.Black;
                执行(input);
            }
        }

        private static void 执行(string input)
        {
            throw new NotImplementedException();
        }
    }
}
很简单,就是想通过它来手工测试一下插件的插拔灵活性。在程序不停机的情况下,我们要动态地随时更新“执行”程序,甚至从互联网上动态更新本地插件的版本。 然后要为工程引用 System.ComponentModel.Composition 程序集,这样才能使用 MEF。 我们要执行的命令,应该是可以动态插拔的。所以其接口定义为 ICommand
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;

namespace ConsoleApplication1
{
    public interface ICommand
    {
        string Execute(string input);
    }

    public interface ICommandMedaData
    {
        string CommandName { get; }
    }

    public class CommandDB
    {
        [ImportMany]
        public IEnumerable<Lazy<ICommand, ICommandMedaData>> Commands;
    }
}
这里的 CommandDB,定义了我们的插件的宿主,所有找到的插件将来都放到这里,并且我们使用 Lazy<,>模式推迟到真正执行一个命令时才去实例化它。 接下来的代码,我其实将 MEF 默认的那几行加载方法重新封装了一下,封装成一个比较容易理解的方法 ImportObject。主程序使用这个方法所查找到的插件,就可以按照 ICommand 接口去调用插件了。最终主程序就是这个样子
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            db = ImportObject<CommandDB>(".");   //这里的点(.)表示当前应用程序执行目录,从这个目录中查找所有插件。
            while (true)
            {
                Console.BackgroundColor = ConsoleColor.DarkBlue;
                Console.WriteLine("请输入命令和参数:");
                var input = Console.ReadLine();
                Console.BackgroundColor = ConsoleColor.Black;
                执行(input);
                Console.WriteLine();
                Console.WriteLine();
            }
        }

        private static CommandDB db;

        private static void 执行(string input)
        {
            var ps = input.Split(new char[] { ' ' }, 2);
            foreach (var c in db.Commands)
            {
                if (c.Metadata.CommandName == ps[0])
                {
                    Console.WriteLine(c.Value.Execute(ps.Length == 2 ? ps[1] : null));
                    return;
                }
            }
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("找不到可执行的命令处理程序。");
            Console.ForegroundColor = ConsoleColor.White;
        }

        /// <summary>
        /// 创建T类型对象实例,然后自动填写此对象实例中需要的所有插件(包括插件中间接引用的子插件)。
        /// </summary>
        public static T ImportObject<T>(string dir) where T : new()
        {
            var obj = new T();
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new DirectoryCatalog(dir));
            catalog.Catalogs.Add(new DirectoryCatalog(dir, "*.exe"));
            new CompositionContainer(catalog).ComposeParts(obj);
            return obj;
        }
    }
}
其实整个程序到底,其引擎就设计完成了。一个程序的引擎就搭建完毕了。
回复
普通的插件是什么插件?如何写?
回复
lccleo 2016-06-28
引用 16 楼 sp1234 的回复:
[quote=引用 4 楼 lccleo 的回复:] 还有个问题 像魔兽世界这种大型软件 每次更新不可能让你重新下载整个软件 都是给个补丁 然后打上去 怎么在C#里面是怎么实现的 比如我有个窗体上面有个黑色button 我弄个UI补丁 让这个button变成蓝色 这种东西有思路吗?
你可以使用 MEF,写上几行代码,就能搞定。 可以在主程序“不停机”的情况下,动态插拔插件(所谓拔除,其实就是再次让MEF到特定的文件目录里边搜索一次插件)。[/quote] 我在网上看了下MEF的demo 不知道这和普通的插件区别在哪里? 请指教
回复
引用 4 楼 lccleo 的回复:
还有个问题 像魔兽世界这种大型软件 每次更新不可能让你重新下载整个软件 都是给个补丁 然后打上去 怎么在C#里面是怎么实现的 比如我有个窗体上面有个黑色button 我弄个UI补丁 让这个button变成蓝色 这种东西有思路吗?
你可以使用 MEF,写上几行代码,就能搞定。 可以在主程序“不停机”的情况下,动态插拔插件(所谓拔除,其实就是再次让MEF到特定的文件目录里边搜索一次插件)。
回复
没有规矩不成方圆。所以有基本的一个接口(仅仅是 interface 而已)作为引入插件的“标准”,这是必须的。这不但不算什么“劣势”,而且有一个“干货”做标准,可以避免胡乱解释接口,比较那些胡乱解释的“基于字符串的弱类型东西”来说,当然是优势。 对你来说,插件不是唯一要注意的东西。插件的宿主是首先要被重视的。 插件的宿主,其设计目标往往是“永不停机”,这就是优势!
回复
引用 4 楼 lccleo 的回复:
还有个问题 像魔兽世界这种大型软件 每次更新不可能让你重新下载整个软件 都是给个补丁 然后打上去 怎么在C#里面是怎么实现的 比如我有个窗体上面有个黑色button 我弄个UI补丁 让这个button变成蓝色 这种东西有思路吗?
你说的很对。抽个时间,我给你写个非常简单、异常稳定的“插件热更新”代码。使用 MEF,其实写上100行代码,也就搞定了。你可以先自己研究一下。
回复
lccleo 2016-06-22
还有其他回答吗
回复
黑娃 2016-06-21
引用 8 楼 lccleo 的回复:
[quote=引用 7 楼 falcomavin 的回复:] [quote=引用 4 楼 lccleo 的回复:] 还有个问题 像魔兽世界这种大型软件 每次更新不可能让你重新下载整个软件 都是给个补丁 然后打上去 怎么在C#里面是怎么实现的 比如我有个窗体上面有个黑色button 我弄个UI补丁 让这个button变成蓝色 这种东西有思路吗?
更新和插件其实没有直接关系,大型软件都是模块化的,更新通常只涉及到整个软件中的部分文件,因此只需要做一个更新包打包需要更新的文件就可以了[/quote] 那对于winform 可以模块化吗?[/quote] 当然可以,分解成一些功能模块形成dll。ui也是可以的,比如你可以专门制作一个皮肤模块来改变控件外形
回复
我是飞云 2016-06-21
DLL共存,那不叫插件,叫多站点共存。
回复
lccleo 2016-06-21
引用 7 楼 falcomavin 的回复:
[quote=引用 4 楼 lccleo 的回复:] 还有个问题 像魔兽世界这种大型软件 每次更新不可能让你重新下载整个软件 都是给个补丁 然后打上去 怎么在C#里面是怎么实现的 比如我有个窗体上面有个黑色button 我弄个UI补丁 让这个button变成蓝色 这种东西有思路吗?
更新和插件其实没有直接关系,大型软件都是模块化的,更新通常只涉及到整个软件中的部分文件,因此只需要做一个更新包打包需要更新的文件就可以了[/quote] 那对于winform 可以模块化吗?
回复
黑娃 2016-06-21
引用 4 楼 lccleo 的回复:
还有个问题 像魔兽世界这种大型软件 每次更新不可能让你重新下载整个软件 都是给个补丁 然后打上去 怎么在C#里面是怎么实现的 比如我有个窗体上面有个黑色button 我弄个UI补丁 让这个button变成蓝色 这种东西有思路吗?
更新和插件其实没有直接关系,大型软件都是模块化的,更新通常只涉及到整个软件中的部分文件,因此只需要做一个更新包打包需要更新的文件就可以了
回复
加载更多回复
相关推荐
发帖
C#
创建于2007-09-28

10.6w+

社区成员

.NET技术 C#
申请成为版主
帖子事件
创建了帖子
2016-06-21 10:03
社区公告

让您成为最强悍的C#开发者