实现.net下的动态代理

一、动态代理可以解决哪些问题

 

本文描述的动态代理可以解决以下问题:

 

问题1:接口约束问题

 

场景AComboBox类与ToolStripComboBox类的行为大部分相似,它们却不共享某个粒度较大的接口,以至于对这两个类的操作代码难以公用。

 

场景B:在泛型程序中,我们必需为泛型类型声明一个接口约束,才能使用该类型所对应接口约束的方法与属性。这样以来有一个问题:存在接口A,类型BA中的所有属性和方法签名B都有,但B不是A的实现,不能由B转换为A。而由于某些原因,我们无法获得B的代码或者虽然能够获得B的代码,却由于种种考虑不能更改B的代码;

大多数情况下,可以新写一个类C,使类C继承自B,再让C实现接口A。然而,其一,代码量较大;其二,当BSealed时不能继承;其三:不方便使用继承(如:继承后,IDE中的设计器无法给新类提供支持)。

 

此时,我们希望动态代理能解决这个问题:

 

假定我们有一个接口:

 

1      public   interface  TInterface
2 ExpandedBlockStart.gifContractedBlock.gif     {
3ExpandedSubBlockStart.gifContractedSubBlock.gif        String A get; }
4        String B(Object a, Object b, Object c, Object d, Object e);
5    }

6

 

有一个类:

 

 1      public   class  ClassA
 2 ExpandedBlockStart.gifContractedBlock.gif     {
 3        public String A 
 4ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 5            get
 6ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 7                return String.Empty;
 8            }

 9        }

10
11        public String B(Object a, Object b, Object c, Object d, Object e)
12ExpandedSubBlockStart.gifContractedSubBlock.gif        {
13            return String.Empty;
14        }

15    }

16
17

 

我们需要这个动态代理类(假定为 TypeTemplate)提供一个方法,能够方便的生成一个代理类实例,将ClassA的实例包装一下,转变为 TInterface 的实例:

 

1          public   static  TInterface Create < TInterface, TImple > (TImple instance) 
2              where  TInterface :  class
3              where  TImple :  class
4 ExpandedBlockStart.gifContractedBlock.gif         {
5            ……
6        }

7

 

该方法生成一个实现TInterface的动态类的实例。对于TInterface中的每一个属性或方法,该实例直接调用instance实例中具有同样签名的属性或方法,如果没有匹配的属性或方法,则抛出NotImplementedException异常。

 

问题2:快速生成现有实例类的Wrapper

 

场景C:假定接口TInterface10个方法,类TImple中有9个方法与TInterface对应,为了实现TInterface,需要新写一个Wrapper类。最方便的写法是令Wrapper继承自TImple且实现接口TInterfaceWrapper只需新添实现那个未实现的方法即可。然而,如前面所述,很多情况下使用继承不是最佳选择。不用继承的话,则需要Wrapper类实现TInterface的全部10个方法,枯燥又乏味。

 

此时,我们期待存在这样一种动态代理:它能够将几个实例BCD一起打包,使它适合接口A。这样一来,针对场景C,我们只需要写一个简单的Wrapper类,实现那个未实现的方法,然后与TImple实例一起由动态代理工厂打包生成一个新的代理类实例即可。

 

即:我们需要动态代理工厂TypeTemplate能够提供以下方法:

 

1          public   static  TInterface Create < TInterface > (TInterface instance,  params  Object[] impleInstances)
2              where  TInterface :  class
3 ExpandedBlockStart.gifContractedBlock.gif         {
4            …
5        }

6

 

该方法生成一个实现TInterface的类实例。对于TInterface中的每一个属性或方法,该实例依次从impleInstances中寻找具有同样签名的属性或方法,如果没有匹配的属性或方法,则抛出NotImplementedException异常。

 

问题3AOP应用

 

场景D:当需要对方法进行拦截时我们需要动态代理。

 

AOP是动态代理最经典的应用,无需赘述。

 

这种情况下,我们需要 TypeTemplate 类提供以下的方法向代理类中加入钩子:

 

1          public   delegate   void  Handler < TImple > (TImple imple)  where  TImple:  class ;
2
3          public   static  TInterface CreateIntercepted < TInterface, TImple > (TImple instance, Handler < TImple >  before, Handler < TImple >  after)
4              where  TInterface :  class
5              where  TImple :  class
6 ExpandedBlockStart.gifContractedBlock.gif         {
7            …
8        }

9

 

二、实现

 

 

下面来实现动态代理类工厂TypeTemplate。由于时间有限,只实现 static TInterface Create<TInterface, TImple>(TImple instance) 方法原型。其它几个静态方法可以用类似的方式实现。

 

实现思路:

 

对于问题1中的接口TInterface和类ClassA,通过Emit生成如下类型InterfaceImple_ClassAIL代码。

 

 

 1      public   class  InterfaceImple_ClassA : TInterface
 2 ExpandedBlockStart.gifContractedBlock.gif     {
 3        private ClassA __wrappedInstance;
 4
 5        public InterfaceImple_ClassA(ClassA instance)
 6ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 7            __wrappedInstance = instance;
 8        }

 9
10        public String A
11ExpandedSubBlockStart.gifContractedSubBlock.gif        {
12ExpandedSubBlockStart.gifContractedSubBlock.gif            get return __wrappedInstance.A; }
13ExpandedSubBlockStart.gifContractedSubBlock.gif            set throw new NotImplementedException(); }
14        }

15
16        public String B(Object a, Object b, Object c, Object d, Object e)
17ExpandedSubBlockStart.gifContractedSubBlock.gif        {
18            return __wrappedInstance.B(a,b,c,d,e);
19        }

20    }

21
22

 

假定生成上面这个类型的方法为:

 

1          public   static  Type DynamicTypeGen < TInterface, TImple > ()
2              where  TInterface:  class   where  TImple :  class
3
4

 

则可用Activator轻松生成动态代理类实例:

 

1          public   static  TInterface Create < TInterface, TImple > (TImple instance) 
2              where  TInterface :  class
3              where  TImple :  class
4 ExpandedBlockStart.gifContractedBlock.gif         {
5            Type type = DynamicTypeGen<TInterface, TImple>();
6            return Activator.CreateInstance(type, instance) as TInterface;
7        }

8

 

下面是具体代码:

 

 

ContractedBlock.gif ExpandedBlockStart.gif Code
  1using System;
  2using System.Collections.Generic;
  3using System.Text;
  4using System.Reflection;
  5using System.Reflection.Emit;
  6
  7namespace Orc.Generics
  8ExpandedBlockStart.gifContractedBlock.gif{
  9    public sealed class TypeTemplate
 10ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 11        public delegate void Handler<TImple>(TImple imple) where TImple: class;
 12
 13        public static TInterface Create<TInterface, TImple>(TImple instance) 
 14            where TInterface : class
 15            where TImple : class
 16ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 17            Type type = DynamicTypeGen<TInterface, TImple>();
 18            return Activator.CreateInstance(type, instance) as TInterface;
 19        }

 20
 21        public static TInterface Create<TInterface>(TInterface instance, params Object[] impleInstances)
 22            where TInterface : class
 23ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 24            throw new NotImplementedException();
 25        }

 26
 27        public static TInterface CreateIntercepted<TInterface, TImple>(TImple instance, Handler<TImple> before, Handler<TImple> after)
 28            where TInterface : class
 29            where TImple : class
 30ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 31            throw new NotImplementedException();
 32        }

 33
 34        public static Type DynamicTypeGen<TInterface, TImple>()
 35            where TInterface: class where TImple : class
 36ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 37            Type tInterface = typeof(TInterface);
 38            Type tImple = typeof(TImple);
 39
 40            PropertyInfo[] pisInterface = tInterface.GetProperties(BindingFlags.Public | BindingFlags.Instance);
 41            MethodInfo[] misInterface = tInterface.GetMethods(BindingFlags.Public | BindingFlags.Instance);
 42            List<MethodInfo> misInterfaceList = new List<MethodInfo>();
 43            foreach (var item in misInterface)
 44ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 45                if (item.IsSpecialName == false) misInterfaceList.Add(item);
 46            }

 47
 48            MethodInfo[] misImple = tImple.GetMethods(BindingFlags.Public | BindingFlags.Instance);
 49            AssemblyName aName = new AssemblyName("Orc.Generics.DynamicTypes");
 50            AssemblyBuilder ab =
 51                AppDomain.CurrentDomain.DefineDynamicAssembly(
 52                    aName,
 53                    AssemblyBuilderAccess.RunAndSave);
 54            ModuleBuilder mb =
 55                ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
 56            TypeBuilder tb = mb.DefineType(GetDynamicTypeName<TInterface, TImple>(),
 57ExpandedSubBlockStart.gifContractedSubBlock.gif                TypeAttributes.Public, nullnew Type[] { tInterface });
 58            FieldBuilder fbInstance = tb.DefineField(
 59                "__wrappedInstance",
 60                tImple,
 61                FieldAttributes.Private);
 62
 63            ConstructorBuilder ctor1 = tb.DefineConstructor(
 64                MethodAttributes.Public,
 65                CallingConventions.Standard,
 66ExpandedSubBlockStart.gifContractedSubBlock.gif                new Type[] {tImple});
 67
 68            ILGenerator ctor1IL = ctor1.GetILGenerator();
 69            ctor1IL.Emit(OpCodes.Ldarg_0);
 70            ctor1IL.Emit(OpCodes.Call,
 71                typeof(object).GetConstructor(Type.EmptyTypes));
 72            ctor1IL.Emit(OpCodes.Ldarg_0);
 73            ctor1IL.Emit(OpCodes.Ldarg_1);
 74            ctor1IL.Emit(OpCodes.Stfld, fbInstance);
 75            ctor1IL.Emit(OpCodes.Ret);
 76
 77            foreach (var item in pisInterface)
 78ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 79                MethodInfo getMi = FindGetMethodInfo(misImple, item);
 80                MethodInfo setMi = FindSetMethodInfo(misImple, item);
 81                CreateProperty(tb, fbInstance, item, getMi, setMi);
 82            }

 83
 84            foreach (var item in misInterfaceList)
 85ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 86                MethodInfo instanceMi = FindMethodInfo(misImple, item);
 87                CreateMethod(tb, fbInstance, item, instanceMi);
 88            }

 89            
 90            return tb.CreateType();
 91        }

 92
 93        private static MethodInfo FindGetMethodInfo(MethodInfo[] miList, PropertyInfo pi)
 94ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 95            foreach (var item in miList)
 96ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 97                if (item.Name.Equals("get_" + pi.Name) && item.IsSpecialName) return item;
 98            }

 99
100            return null;
101        }

102
103        private static MethodInfo FindSetMethodInfo(MethodInfo[] miList, PropertyInfo pi)
104ExpandedSubBlockStart.gifContractedSubBlock.gif        {
105            foreach (var item in miList)
106ExpandedSubBlockStart.gifContractedSubBlock.gif            {
107                if (item.Name.Equals("set_" + pi.Name) && item.IsSpecialName) return item;
108            }

109            
110            return null;
111        }

112
113        private static MethodInfo FindMethodInfo(MethodInfo[] miList, MethodInfo mi)
114ExpandedSubBlockStart.gifContractedSubBlock.gif        {
115            foreach (var item in miList)
116ExpandedSubBlockStart.gifContractedSubBlock.gif            {
117                if (MethodInfoEqual(item,mi)) return item;
118            }

119
120            return null;
121        }

122
123        private static Boolean MethodInfoEqual(MethodInfo mi1, MethodInfo mi2)
124ExpandedSubBlockStart.gifContractedSubBlock.gif        {
125            if (mi1.IsSpecialName == true || mi2.IsSpecialName == truereturn false;
126            if (mi1.Name != mi2.Name) return false;
127            if (mi1.ReturnType != mi2.ReturnType) return false;
128            ParameterInfo[] pis1 = mi1.GetParameters();
129            ParameterInfo[] pis2 = mi2.GetParameters();
130            if (pis1.Length != pis2.Length) return false;
131            for (int i = 0; i < pis1.Length; i++)
132ExpandedSubBlockStart.gifContractedSubBlock.gif            {
133                ParameterInfo pi1 = pis1[i];
134                ParameterInfo pi2 = pis2[i];
135                if (pi1.ParameterType != pi2.ParameterType) return false;
136            }

137            return true;
138        }

139
140        private static void CreateProperty(TypeBuilder tb, FieldBuilder fbInstance, PropertyInfo pi, MethodInfo getMi, MethodInfo setMi)
141ExpandedSubBlockStart.gifContractedSubBlock.gif        {
142            String name = pi.Name;
143            Type type = pi.PropertyType;
144
145            PropertyBuilder pb = tb.DefineProperty(
146                name,
147                PropertyAttributes.HasDefault,
148                type,
149                null);
150
151            MethodAttributes getSetAttr = MethodAttributes.Public |
152                MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final ;
153            MethodBuilder mbGetAccessor = tb.DefineMethod(
154                "get_" + name,
155                getSetAttr,
156                type,
157                Type.EmptyTypes);
158
159            ILGenerator getIL = mbGetAccessor.GetILGenerator();
160            if (getMi == null)
161ExpandedSubBlockStart.gifContractedSubBlock.gif            {
162ExpandedSubBlockStart.gifContractedSubBlock.gif                getIL.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[]{}));
163                getIL.Emit(OpCodes.Throw);
164            }

165            else
166ExpandedSubBlockStart.gifContractedSubBlock.gif            {
167                getIL.Emit(OpCodes.Ldarg_0);
168                getIL.Emit(OpCodes.Ldfld, fbInstance);
169                getIL.Emit(OpCodes.Callvirt, getMi);
170                getIL.Emit(OpCodes.Ret);
171            }

172
173            MethodBuilder mbSetAccessor = tb.DefineMethod(
174                "set_"+ name,
175                getSetAttr,
176                null,
177ExpandedSubBlockStart.gifContractedSubBlock.gif                new Type[] { type });
178
179            ILGenerator setIL = mbSetAccessor.GetILGenerator();
180            if (setMi == null)
181ExpandedSubBlockStart.gifContractedSubBlock.gif            {
182ExpandedSubBlockStart.gifContractedSubBlock.gif                setIL.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[] { }));
183                setIL.Emit(OpCodes.Throw);
184            }

185            else
186ExpandedSubBlockStart.gifContractedSubBlock.gif            {
187                setIL.Emit(OpCodes.Ldarg_0);
188                setIL.Emit(OpCodes.Ldfld, fbInstance);
189                setIL.Emit(OpCodes.Ldarg_1);
190                setIL.Emit(OpCodes.Callvirt, setMi);
191                setIL.Emit(OpCodes.Ret);
192            }

193
194            pb.SetGetMethod(mbGetAccessor);
195            pb.SetSetMethod(mbSetAccessor);
196        }

197
198        private static void CreateMethod(TypeBuilder tb, FieldBuilder fbInstance, MethodInfo mi, MethodInfo instanceMi)
199ExpandedSubBlockStart.gifContractedSubBlock.gif        {
200            List<Type> paramTyleList = new List<Type>();
201            foreach(var item in mi.GetParameters())
202                paramTyleList.Add(item.ParameterType);
203
204            MethodBuilder mb = tb.DefineMethod(
205              mi.Name,
206              MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
207              mi.ReturnType,
208              paramTyleList.ToArray());
209
210            ILGenerator il = mb.GetILGenerator();
211            if (instanceMi == null)
212ExpandedSubBlockStart.gifContractedSubBlock.gif            {
213ExpandedSubBlockStart.gifContractedSubBlock.gif                il.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[] { }));
214                il.Emit(OpCodes.Throw);
215            }

216            else
217ExpandedSubBlockStart.gifContractedSubBlock.gif            {
218                il.Emit(OpCodes.Ldarg_0);
219                il.Emit(OpCodes.Ldfld, fbInstance);
220                switch (paramTyleList.Count)
221ExpandedSubBlockStart.gifContractedSubBlock.gif                {
222                    case 0:
223                        break;
224                    case 1:
225                        il.Emit(OpCodes.Ldarg_1);
226                        break;
227                    case 2:
228                        il.Emit(OpCodes.Ldarg_1);
229                        il.Emit(OpCodes.Ldarg_2);
230                        break;
231                    case 3:
232                        il.Emit(OpCodes.Ldarg_1);
233                        il.Emit(OpCodes.Ldarg_2);
234                        il.Emit(OpCodes.Ldarg_3);
235                        break;
236                    default:
237                        il.Emit(OpCodes.Ldarg_1);
238                        il.Emit(OpCodes.Ldarg_2);
239                        il.Emit(OpCodes.Ldarg_3);
240
241                        Int32 sCount = Math.Min(paramTyleList.Count, 127);
242                        for (int i = 4; i <= sCount; i++)
243ExpandedSubBlockStart.gifContractedSubBlock.gif                        {
244                            il.Emit(OpCodes.Ldarg_S, i);
245                        }

246
247                        for (int i = 128; i <= paramTyleList.Count; i++)
248ExpandedSubBlockStart.gifContractedSubBlock.gif                        {
249                            il.Emit(OpCodes.Ldarg, i);
250                        }

251
252                        break;
253                }

254
255                il.Emit(OpCodes.Callvirt, instanceMi);
256                il.Emit(OpCodes.Ret);
257            }

258        }

259
260        private static String GetDynamicTypeName<TInterface, TImple>()
261            where TInterface : class
262            where TImple : class
263ExpandedSubBlockStart.gifContractedSubBlock.gif        {
264            return "_DynamicTypes" + typeof(TInterface).ToString() + "_" + typeof(TImple);
265        }

266    }

267
268    public interface TInterface
269ExpandedSubBlockStart.gifContractedSubBlock.gif    {
270ExpandedSubBlockStart.gifContractedSubBlock.gif        String A get; }
271        String B(int a, int b);
272    }

273
274    public class ClassA
275ExpandedSubBlockStart.gifContractedSubBlock.gif    {
276        public String A 
277ExpandedSubBlockStart.gifContractedSubBlock.gif        {
278            get
279ExpandedSubBlockStart.gifContractedSubBlock.gif            {
280                return String.Empty;
281            }

282        }

283
284        public String B(int a, int b)
285ExpandedSubBlockStart.gifContractedSubBlock.gif        {
286            return (a+b).ToString();
287        }

288    }

289
290    public class InterfaceImple_ClassA : TInterface
291ExpandedSubBlockStart.gifContractedSubBlock.gif    {
292        private ClassA __wrappedInstance;
293
294        public InterfaceImple_ClassA(ClassA instance)
295ExpandedSubBlockStart.gifContractedSubBlock.gif        {
296            __wrappedInstance = instance;
297        }

298
299        public String A
300ExpandedSubBlockStart.gifContractedSubBlock.gif        {
301ExpandedSubBlockStart.gifContractedSubBlock.gif            get return __wrappedInstance.A; }
302ExpandedSubBlockStart.gifContractedSubBlock.gif            set throw new NotImplementedException(); }
303        }

304
305        public String B(int a, int b)
306ExpandedSubBlockStart.gifContractedSubBlock.gif        {
307            return __wrappedInstance.B(a,b);
308        }

309    }

310
311    public class InterfaceImple_ClassA_Factory
312ExpandedSubBlockStart.gifContractedSubBlock.gif    {
313        public readonly String Name = "InterfaceImple_ClassA_Factory";
314        public InterfaceImple_ClassA Create(ClassA a)
315ExpandedSubBlockStart.gifContractedSubBlock.gif        {
316            return new InterfaceImple_ClassA(a);
317        }

318    }

319}

320

 

单元测试代码:

 

 1         [TestMethod]
 2          public   void  TestCreate()
 3 ExpandedBlockStart.gifContractedBlock.gif         {
 4            ClassA a = new ClassA();
 5            TInterface i = TypeTemplate.Create<TInterface, ClassA>(a);
 6            Assert.AreNotEqual(null, i);
 7            Assert.AreEqual(String.Empty, i.A);
 8            Assert.AreEqual("3", i.B(12));
 9        }

10

 

 

三、说明

 

1         上文只是 TypeTemplate的原型实现,我只进行了简单的单元测试,没有针对全部可能遇见的情况进行测试;在功能上,未生成event的动态代理;

2         static TInterface Create<TInterface>(TInterface instance, params Object[] impleInstances) 这个方法生成的代理类最好继承一个特殊接口,通过该接口能够接入被代理的实例;

3         如果第一部分所述的三个静态方法全部实现了的话,将是一个非常强大的代理工厂;

4         代理类实例的性能接近于直接调用相关方法或属性的性能,性能极佳;

5         代理类实例的生成是通过反射生成的,在性能上有很大的提升空间。可以通过emit动态生成一个工厂类。如,针对上面的ClassA,生成如下工厂类:

 

1      public   class  InterfaceImple_ClassA_Factory
2 ExpandedBlockStart.gifContractedBlock.gif     {
3        public readonly String Name = "InterfaceImple_ClassA_Factory";
4        public InterfaceImple_ClassA Create(ClassA a)
5ExpandedSubBlockStart.gifContractedSubBlock.gif        {
6            return new InterfaceImple_ClassA(a);
7        }

8    }

9

 

         然后通过反射生成以上工厂类的实例,缓存住。当需要生成新的动态代理实例时,从缓存中查找对应的工厂,生成具体的代理类。

转载于:https://www.cnblogs.com/xiaotie/archive/2009/02/01/1381825.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
.NET 中,可以使用 HttpListener 类来实现搭建代理服务器。具体步骤如下: 1. 创建 HttpListener 实例,设置代理服务器监听的地址和端口号。 2. 使用 HttpListener 响应客户端请求,获取客户端请求的数据。 3. 解析客户端请求的数据,获取需要代理的目标服务器地址和端口号。 4. 使用 HttpClient 类向目标服务器发送请求,并获取目标服务器响应的数据。 5. 将目标服务器响应的数据返回给客户端。 需要注意的是,在实现代理服务器的过程中,需要处理客户端请求和目标服务器响应中的 HTTP 头信息,并且需要处理代理服务器和目标服务器之间的连接管理。 以下是一个简单的 .NET 代理服务器实现的代码示例,仅供参考: ```csharp using System; using System.Net; using System.Net.Http; class ProxyServer { private HttpListener _listener = new HttpListener(); public void Start(string prefix) { _listener.Prefixes.Add(prefix); _listener.Start(); while (true) { var context = _listener.GetContext(); var request = context.Request; var response = context.Response; // 解析客户端请求的目标服务器地址和端口号 var targetUri = new Uri(request.Headers["X-Target-Uri"]); // 使用 HttpClient 发送请求到目标服务器 using (var client = new HttpClient()) { var targetRequest = new HttpRequestMessage(new HttpMethod(request.HttpMethod), targetUri); targetRequest.Content = new StreamContent(request.InputStream); foreach (var header in request.Headers) { if (!targetRequest.Headers.TryAddWithoutValidation(header.Key, header.Value)) { targetRequest.Content.Headers.TryAddWithoutValidation(header.Key, header.Value); } } var targetResponse = client.SendAsync(targetRequest).Result; // 将目标服务器响应的数据返回给客户端 response.StatusCode = (int)targetResponse.StatusCode; foreach (var header in targetResponse.Headers) { response.Headers[header.Key] = string.Join(",", header.Value); } foreach (var header in targetResponse.Content.Headers) { response.Headers[header.Key] = string.Join(",", header.Value); } targetResponse.Content.CopyToAsync(response.OutputStream).Wait(); } response.Close(); } } public void Stop() { _listener.Stop(); } } class Program { static void Main(string[] args) { var server = new ProxyServer(); server.Start("http://localhost:8080/"); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值