-
Notifications
You must be signed in to change notification settings - Fork 242
Expand file tree
/
Copy pathDispatchingUserDataDescriptor.cs
More file actions
665 lines (570 loc) · 21.9 KB
/
Copy pathDispatchingUserDataDescriptor.cs
File metadata and controls
665 lines (570 loc) · 21.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
using System;
using System.Collections.Generic;
using System.Linq;
using MoonSharp.Interpreter.Compatibility;
using MoonSharp.Interpreter.Interop.Converters;
namespace MoonSharp.Interpreter.Interop.BasicDescriptors
{
/// <summary>
/// An abstract user data descriptor which accepts members described by <see cref="IMemberDescriptor"/> objects and
/// correctly dispatches to them.
/// Metamethods are also by default dispatched to operator overloads and other similar methods - see
/// <see cref="MetaIndex"/> .
/// </summary>
public abstract class DispatchingUserDataDescriptor : IUserDataDescriptor, IOptimizableDescriptor
{
private int m_ExtMethodsVersion = 0;
private Dictionary<string, IMemberDescriptor> m_MetaMembers = new Dictionary<string, IMemberDescriptor>();
private Dictionary<string, IMemberDescriptor> m_Members = new Dictionary<string, IMemberDescriptor>();
/// <summary>
/// The special name used by CLR for indexer getters
/// </summary>
protected const string SPECIALNAME_INDEXER_GET = "get_Item";
/// <summary>
/// The special name used by CLR for indexer setters
/// </summary>
protected const string SPECIALNAME_INDEXER_SET = "set_Item";
/// <summary>
/// The special name used by CLR for explicit cast conversions
/// </summary>
protected const string SPECIALNAME_CAST_EXPLICIT = "op_Explicit";
/// <summary>
/// The special name used by CLR for implicit cast conversions
/// </summary>
protected const string SPECIALNAME_CAST_IMPLICIT = "op_Implicit";
/// <summary>
/// Gets the name of the descriptor (usually, the name of the type described).
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Gets the type this descriptor refers to
/// </summary>
public Type Type { get; private set; }
/// <summary>
/// Gets a human readable friendly name of the descriptor
/// </summary>
public string FriendlyName { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="StandardUserDataDescriptor" /> class.
/// </summary>
/// <param name="type">The type this descriptor refers to.</param>
/// <param name="friendlyName">A friendly name for the type, or null.</param>
protected DispatchingUserDataDescriptor(Type type, string friendlyName = null)
{
Type = type;
Name = type.FullName;
FriendlyName = friendlyName ?? type.Name;
}
/// <summary>
/// Adds a member to the meta-members list.
/// </summary>
/// <param name="name">The name of the metamethod.</param>
/// <param name="desc">The desc.</param>
/// <exception cref="System.ArgumentException">
/// Thrown if a name conflict is detected and one of the conflicting members does not support overloads.
/// </exception>
public void AddMetaMember(string name, IMemberDescriptor desc)
{
if (desc != null)
AddMemberTo(m_MetaMembers, name, desc);
}
/// <summary>
/// Adds a DynValue as a member
/// </summary>
/// <param name="name">The name.</param>
/// <param name="value">The value.</param>
public void AddDynValue(string name, DynValue value)
{
var desc = new DynValueMemberDescriptor(name, value);
AddMemberTo(m_Members, name, desc);
}
/// <summary>
/// Adds a property to the member list
/// </summary>
/// <param name="name">The name.</param>
/// <param name="desc">The descriptor.</param>
/// <exception cref="System.ArgumentException">
/// Thrown if a name conflict is detected and one of the conflicting members does not support overloads.
/// </exception>
public void AddMember(string name, IMemberDescriptor desc)
{
if (desc != null)
AddMemberTo(m_Members, name, desc);
}
/// <summary>
/// Gets the member names.
/// </summary>
public IEnumerable<string> MemberNames
{
get { return m_Members.Keys; }
}
/// <summary>
/// Gets the members.
/// </summary>
public IEnumerable<KeyValuePair<string, IMemberDescriptor>> Members
{
get { return m_Members; }
}
/// <summary>
/// Finds the member with a given name. If not found, null is returned.
/// </summary>
/// <param name="memberName">Name of the member.</param>
/// <returns></returns>
public IMemberDescriptor FindMember(string memberName)
{
return m_Members.GetOrDefault(memberName);
}
/// <summary>
/// Removes the member with a given name. In case of overloaded functions, all overloads are removed.
/// </summary>
/// <param name="memberName">Name of the member.</param>
public void RemoveMember(string memberName)
{
m_Members.Remove(memberName);
}
/// <summary>
/// Gets the meta member names.
/// </summary>
public IEnumerable<string> MetaMemberNames
{
get { return m_MetaMembers.Keys; }
}
/// <summary>
/// Gets the meta members.
/// </summary>
public IEnumerable<KeyValuePair<string, IMemberDescriptor>> MetaMembers
{
get { return m_MetaMembers; }
}
/// <summary>
/// Finds the meta member with a given name. If not found, null is returned.
/// </summary>
/// <param name="memberName">Name of the member.</param>
public IMemberDescriptor FindMetaMember(string memberName)
{
return m_MetaMembers.GetOrDefault(memberName);
}
/// <summary>
/// Removes the meta member with a given name. In case of overloaded functions, all overloads are removed.
/// </summary>
/// <param name="memberName">Name of the member.</param>
public void RemoveMetaMember(string memberName)
{
m_MetaMembers.Remove(memberName);
}
private void AddMemberTo(Dictionary<string, IMemberDescriptor> members, string name, IMemberDescriptor desc)
{
IOverloadableMemberDescriptor odesc = desc as IOverloadableMemberDescriptor;
if (odesc != null && !(desc is MethodMemberDescriptorCoroutine))
{
if (members.ContainsKey(name))
{
OverloadedMethodMemberDescriptor overloads = members[name] as OverloadedMethodMemberDescriptor;
if (overloads != null)
overloads.AddOverload(odesc);
else
throw new ArgumentException(string.Format("Multiple members named {0} are being added to type {1} and one or more of these members do not support overloads.", name, this.Type.FullName));
}
else
{
members.Add(name, new OverloadedMethodMemberDescriptor(name, this.Type, odesc));
}
}
else
{
if (members.ContainsKey(name))
{
throw new ArgumentException(string.Format("Multiple members named {0} are being added to type {1} and one or more of these members do not support overloads.", name, this.Type.FullName));
}
else
{
members.Add(name, desc);
}
}
}
/// <summary>
/// Performs an "index" "get" operation. This tries to resolve minor variations of member names.
/// </summary>
/// <param name="script">The script originating the request</param>
/// <param name="obj">The object (null if a static request is done)</param>
/// <param name="index">The index.</param>
/// <param name="isDirectIndexing">If set to true, it's indexed with a name, if false it's indexed through brackets.</param>
/// <returns></returns>
public virtual DynValue Index(Script script, object obj, DynValue index, bool isDirectIndexing)
{
if (!isDirectIndexing)
{
IMemberDescriptor mdesc = m_Members
.GetOrDefault(SPECIALNAME_INDEXER_GET)
.WithAccessOrNull(MemberDescriptorAccess.CanExecute);
if (mdesc != null)
return ExecuteIndexer(mdesc, script, obj, index, null);
}
index = index.ToScalar();
if (index.Type != DataType.String)
return null;
DynValue v = TryIndex(script, obj, index.String);
if (v == null && (Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.UpperFirstLetter) == FuzzySymbolMatchingBehavior.UpperFirstLetter) v = TryIndex(script, obj, UpperFirstLetter(index.String));
if (v == null && (Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.Camelify) == FuzzySymbolMatchingBehavior.Camelify) v = TryIndex(script, obj, Camelify(index.String));
if (v == null && (Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.PascalCase) == FuzzySymbolMatchingBehavior.PascalCase) v = TryIndex(script, obj, UpperFirstLetter(Camelify(index.String)));
if (v == null && m_ExtMethodsVersion < UserData.GetExtensionMethodsChangeVersion())
{
m_ExtMethodsVersion = UserData.GetExtensionMethodsChangeVersion();
v = TryIndexOnExtMethod(script, obj, index.String);
if (v == null && (Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.UpperFirstLetter) == FuzzySymbolMatchingBehavior.UpperFirstLetter) v = TryIndexOnExtMethod(script, obj, UpperFirstLetter(index.String));
if (v == null && (Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.Camelify) == FuzzySymbolMatchingBehavior.Camelify) v = TryIndexOnExtMethod(script, obj, Camelify(index.String));
if (v == null && (Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.PascalCase) == FuzzySymbolMatchingBehavior.PascalCase) v = TryIndexOnExtMethod(script, obj, UpperFirstLetter(Camelify(index.String)));
}
return v;
}
/// <summary>
/// Tries to perform an indexing operation by checking newly added extension methods for the given indexName.
/// </summary>
/// <param name="script">The script.</param>
/// <param name="obj">The object.</param>
/// <param name="indexName">Member name to be indexed.</param>
/// <returns></returns>
/// <exception cref="System.NotImplementedException"></exception>
private DynValue TryIndexOnExtMethod(Script script, object obj, string indexName)
{
List<IOverloadableMemberDescriptor> methods = UserData.GetExtensionMethodsByNameAndType(indexName, this.Type);
if (methods != null && methods.Count > 0)
{
var ext = new OverloadedMethodMemberDescriptor(indexName, this.Type);
ext.SetExtensionMethodsSnapshot(UserData.GetExtensionMethodsChangeVersion(), methods);
m_Members.Add(indexName, ext);
return DynValue.NewCallback(ext.GetCallback(script, obj));
}
return null;
}
/// <summary>
/// Determines whether the descriptor contains the specified member (by exact name)
/// </summary>
/// <param name="exactName">Name of the member.</param>
/// <returns></returns>
public bool HasMember(string exactName)
{
return m_Members.ContainsKey(exactName);
}
/// <summary>
/// Determines whether the descriptor contains the specified member in the meta list (by exact name)
/// </summary>
/// <param name="exactName">Name of the meta-member.</param>
/// <returns></returns>
public bool HasMetaMember(string exactName)
{
return m_MetaMembers.ContainsKey(exactName);
}
/// <summary>
/// Tries to perform an indexing operation by checking methods and properties for the given indexName
/// </summary>
/// <param name="script">The script.</param>
/// <param name="obj">The object.</param>
/// <param name="indexName">Member name to be indexed.</param>
/// <returns></returns>
protected virtual DynValue TryIndex(Script script, object obj, string indexName)
{
IMemberDescriptor desc;
if (m_Members.TryGetValue(indexName, out desc))
{
return desc.GetValue(script, obj);
}
return null;
}
/// <summary>
/// Performs an "index" "set" operation. This tries to resolve minor variations of member names.
/// </summary>
/// <param name="script">The script originating the request</param>
/// <param name="obj">The object (null if a static request is done)</param>
/// <param name="index">The index.</param>
/// <param name="value">The value to be set</param>
/// <param name="isDirectIndexing">If set to true, it's indexed with a name, if false it's indexed through brackets.</param>
/// <returns></returns>
public virtual bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing)
{
if (!isDirectIndexing)
{
IMemberDescriptor mdesc = m_Members
.GetOrDefault(SPECIALNAME_INDEXER_SET)
.WithAccessOrNull(MemberDescriptorAccess.CanExecute);
if (mdesc != null)
{
ExecuteIndexer(mdesc, script, obj, index, value);
return true;
}
}
index = index.ToScalar();
if (index.Type != DataType.String)
return false;
bool v = TrySetIndex(script, obj, index.String, value);
if (!v && (Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.UpperFirstLetter) == FuzzySymbolMatchingBehavior.UpperFirstLetter) v = TrySetIndex(script, obj, UpperFirstLetter(index.String), value);
if (!v && (Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.Camelify) == FuzzySymbolMatchingBehavior.Camelify) v = TrySetIndex(script, obj, Camelify(index.String), value);
if (!v && (Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.PascalCase) == FuzzySymbolMatchingBehavior.PascalCase) v = TrySetIndex(script, obj, UpperFirstLetter(Camelify(index.String)), value);
return v;
}
/// <summary>
/// Tries to perform an indexing "set" operation by checking methods and properties for the given indexName
/// </summary>
/// <param name="script">The script.</param>
/// <param name="obj">The object.</param>
/// <param name="indexName">Member name to be indexed.</param>
/// <param name="value">The value.</param>
/// <returns></returns>
protected virtual bool TrySetIndex(Script script, object obj, string indexName, DynValue value)
{
IMemberDescriptor descr = m_Members.GetOrDefault(indexName);
if (descr != null)
{
descr.SetValue(script, obj, value);
return true;
}
else
{
return false;
}
}
void IOptimizableDescriptor.Optimize()
{
foreach (var m in this.m_MetaMembers.Values.OfType<IOptimizableDescriptor>())
m.Optimize();
foreach (var m in this.m_Members.Values.OfType<IOptimizableDescriptor>())
m.Optimize();
}
/// <summary>
/// Converts the specified name from underscore_case to camelCase.
/// Just a wrapper over the <see cref="DescriptorHelpers"/> method with the same name,
/// </summary>
/// <param name="name">The name.</param>
/// <returns></returns>
protected static string Camelify(string name)
{
return DescriptorHelpers.Camelify(name);
}
/// <summary>
/// Converts the specified name to one with an uppercase first letter (something to Something).
/// Just a wrapper over the <see cref="DescriptorHelpers"/> method with the same name,
/// </summary>
/// <param name="name">The name.</param>
/// <returns></returns>
protected static string UpperFirstLetter(string name)
{
return DescriptorHelpers.UpperFirstLetter(name);
}
/// <summary>
/// Converts this userdata to string
/// </summary>
/// <param name="obj">The object.</param>
/// <returns></returns>
public virtual string AsString(object obj)
{
return (obj != null) ? obj.ToString() : null;
}
/// <summary>
/// Executes the specified indexer method.
/// </summary>
/// <param name="mdesc">The method descriptor</param>
/// <param name="script">The script.</param>
/// <param name="obj">The object.</param>
/// <param name="index">The indexer parameter</param>
/// <param name="value">The dynvalue to set on a setter, or null.</param>
/// <returns></returns>
/// <exception cref="System.NotImplementedException"></exception>
protected virtual DynValue ExecuteIndexer(IMemberDescriptor mdesc, Script script, object obj, DynValue index, DynValue value)
{
IList<DynValue> values;
if (index.Type == DataType.Tuple)
{
if (value == null)
{
values = index.Tuple;
}
else
{
values = new List<DynValue>(index.Tuple);
values.Add(value);
}
}
else
{
if (value == null)
{
values = new DynValue[] { index };
}
else
{
values = new DynValue[] { index, value };
}
}
CallbackArguments args = new CallbackArguments(values, false);
ScriptExecutionContext execCtx = script.CreateDynamicExecutionContext();
DynValue v = mdesc.GetValue(script, obj);
if (v.Type != DataType.ClrFunction)
throw new ScriptRuntimeException("a clr callback was expected in member {0}, while a {1} was found", mdesc.Name, v.Type);
return v.Callback.ClrCallback(execCtx, args);
}
/// <summary>
/// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality,
/// it should return "null" (not a nil).
/// See <see cref="IUserDataDescriptor.MetaIndex" /> for further details.
///
/// If a method exists marked with <see cref="MoonSharpUserDataMetamethodAttribute" /> for the specific
/// metamethod requested, that method is returned.
///
/// If the above fails, the following dispatching occur:
///
/// __add, __sub, __mul, __div, __mod and __unm are dispatched to C# operator overloads (if they exist)
/// __eq is dispatched to System.Object.Equals.
/// __lt and __le are dispatched IComparable.Compare, if the type implements IComparable or IComparable{object}
/// __len is dispatched to Length and Count properties, if those exist.
/// __iterator is handled if the object implements IEnumerable or IEnumerator.
/// __tonumber is dispatched to implicit or explicit conversion operators to standard numeric types.
/// __tobool is dispatched to an implicit or explicit conversion operator to bool. If that fails, operator true is used.
///
/// <param name="script">The script originating the request</param>
/// <param name="obj">The object (null if a static request is done)</param>
/// <param name="metaname">The name of the metamember.</param>
/// </summary>
/// <returns></returns>
public virtual DynValue MetaIndex(Script script, object obj, string metaname)
{
IMemberDescriptor desc = m_MetaMembers.GetOrDefault(metaname);
if (desc != null)
{
return desc.GetValue(script, obj);
}
switch (metaname)
{
case "__add":
return DispatchMetaOnMethod(script, obj, "op_Addition");
case "__sub":
return DispatchMetaOnMethod(script, obj, "op_Subtraction");
case "__mul":
return DispatchMetaOnMethod(script, obj, "op_Multiply");
case "__div":
return DispatchMetaOnMethod(script, obj, "op_Division");
case "__mod":
return DispatchMetaOnMethod(script, obj, "op_Modulus");
case "__unm":
return DispatchMetaOnMethod(script, obj, "op_UnaryNegation");
case "__eq":
return MultiDispatchEqual(script, obj);
case "__lt":
return MultiDispatchLessThan(script, obj);
case "__le":
return MultiDispatchLessThanOrEqual(script, obj);
case "__len":
return TryDispatchLength(script, obj);
case "__tonumber":
return TryDispatchToNumber(script, obj);
case "__tobool":
return TryDispatchToBool(script, obj);
case "__iterator":
return ClrToScriptConversions.EnumerationToDynValue(script, obj);
default:
return null;
}
}
#region MetaMethodsDispatching
private int PerformComparison(object obj, object p1, object p2)
{
IComparable comp = (IComparable)obj;
if (comp != null)
{
if (object.ReferenceEquals(obj, p1))
return comp.CompareTo(p2);
else if (object.ReferenceEquals(obj, p2))
return -comp.CompareTo(p1);
}
throw new InternalErrorException("unexpected case");
}
private DynValue MultiDispatchLessThanOrEqual(Script script, object obj)
{
IComparable comp = obj as IComparable;
if (comp != null)
{
return DynValue.NewCallback(
(context, args) =>
DynValue.NewBoolean(PerformComparison(obj, args[0].ToObject(), args[1].ToObject()) <= 0));
}
return null;
}
private DynValue MultiDispatchLessThan(Script script, object obj)
{
IComparable comp = obj as IComparable;
if (comp != null)
{
return DynValue.NewCallback(
(context, args) =>
DynValue.NewBoolean(PerformComparison(obj, args[0].ToObject(), args[1].ToObject()) < 0));
}
return null;
}
private DynValue TryDispatchLength(Script script, object obj)
{
if (obj == null) return null;
var lenprop = m_Members.GetOrDefault("Length");
if (lenprop != null && lenprop.CanRead() && !lenprop.CanExecute()) return lenprop.GetGetterCallbackAsDynValue(script, obj);
var countprop = m_Members.GetOrDefault("Count");
if (countprop != null && countprop.CanRead() && !countprop.CanExecute()) return countprop.GetGetterCallbackAsDynValue(script, obj);
return null;
}
private DynValue MultiDispatchEqual(Script script, object obj)
{
return DynValue.NewCallback(
(context, args) => DynValue.NewBoolean(CheckEquality(obj, args[0].ToObject(), args[1].ToObject())));
}
private bool CheckEquality(object obj, object p1, object p2)
{
if (obj != null)
{
if (object.ReferenceEquals(obj, p1))
return obj.Equals(p2);
else if (object.ReferenceEquals(obj, p2))
return obj.Equals(p1);
}
if (p1 != null) return p1.Equals(p2);
else if (p2 != null) return p2.Equals(p1);
else return true;
}
private DynValue DispatchMetaOnMethod(Script script, object obj, string methodName)
{
IMemberDescriptor desc = m_Members.GetOrDefault(methodName);
if (desc != null)
{
return desc.GetValue(script, obj);
}
else
return null;
}
private DynValue TryDispatchToNumber(Script script, object obj)
{
foreach (Type t in NumericConversions.NumericTypesOrdered)
{
var name = t.GetConversionMethodName();
var v = DispatchMetaOnMethod(script, obj, name);
if (v != null) return v;
}
return null;
}
private DynValue TryDispatchToBool(Script script, object obj)
{
var name = typeof(bool).GetConversionMethodName();
var v = DispatchMetaOnMethod(script, obj, name);
if (v != null) return v;
return DispatchMetaOnMethod(script, obj, "op_True");
}
#endregion
/// <summary>
/// Determines whether the specified object is compatible with the specified type.
/// Unless a very specific behaviour is needed, the correct implementation is a
/// simple " return type.IsInstanceOfType(obj); "
/// </summary>
/// <param name="type">The type.</param>
/// <param name="obj">The object.</param>
/// <returns></returns>
public virtual bool IsTypeCompatible(Type type, object obj)
{
return Framework.Do.IsInstanceOfType(type, obj);
}
}
}