public void DoBind(VMBehaviour vm, object parameter, GameObject obj, Component component)
            {
                if (component == null)
                {
                    Debugger.LogError("CommandBinder", obj.name + " component null.");
                    return;
                }
                if (string.IsNullOrEmpty(this.Command))
                {
                    Debugger.LogError("CommandBinder", obj.name + " command key empty.");
                    return;
                }
                if (string.IsNullOrEmpty(this.Event))
                {
                    Debugger.LogError("CommandBinder", obj.name + " event key empty.");
                    return;
                }

                // component type reflection
                componentType       = component.GetType();
                componentReflection = ReflectionCache.Singleton[componentType];

                // command type reflection
                command = vm.GetCommand(this.Command);
                if (command == null)
                {
                    Debugger.LogError("CommandBinder", obj.name + " command null.");
                    return;
                }
                commandType = command.GetType().BaseType;
                if (!typeof(BaseCommand).IsAssignableFrom(commandType))
                {
                    Debugger.LogError("CommandBinder", obj.name + " command type error.");
                    return;
                }
                commandReflection = ReflectionCache.Singleton[commandType];

                // source event type reflection
                // TODO: 性能优化 GetValue
                // UGUI 组件的 event 有的是 property,有的不是,所以 field 和 property 都判断
                FieldInfo    sourceEventFieldInfo    = componentReflection.GetField(this.Event);
                PropertyInfo sourceEventPropertyInfo = componentReflection.GetProperty(this.Event);

                if (sourceEventFieldInfo != null)
                {
                    sourceEventType = sourceEventFieldInfo.FieldType.BaseType;
                    sourceEventObj  = sourceEventFieldInfo.GetValue(component);
                }
                if (sourceEventPropertyInfo != null)
                {
                    sourceEventType = sourceEventPropertyInfo.PropertyType.BaseType;
                    sourceEventObj  = sourceEventPropertyInfo.GetValue(component);
                }
                if (sourceEventType == null || sourceEventObj == null)
                {
                    Debugger.LogError("CommandBinder", obj.name + " event null.");
                    return;
                }
                if (!typeof(UnityEngine.Events.UnityEventBase).IsAssignableFrom(sourceEventType))
                {
                    Debugger.LogError("CommandBinder", obj.name + " event type error.");
                    return;
                }
                sourceEventReflection = ReflectionCache.Singleton[sourceEventType];

                // 判断 event 和 command 参数类型是否匹配
                bool genericTypeExplicit = true;

                while (true)
                {
                    if (sourceEventType.IsGenericType != commandType.IsGenericType)
                    {
                        genericTypeExplicit = false;
                        break;
                    }

                    Type[] sourceEventGenericTypes = sourceEventReflection.GetGenericArguments();
                    Type[] commandGenericTypes     = commandReflection.GetGenericArguments();
                    if (sourceEventGenericTypes.Length != commandGenericTypes.Length)
                    {
                        genericTypeExplicit = false;
                        break;
                    }
                    for (int j = 0; j < sourceEventGenericTypes.Length; j++)
                    {
                        if (sourceEventGenericTypes[j] != commandGenericTypes[j])
                        {
                            genericTypeExplicit = false;
                            break;
                        }
                    }
                    break;
                }
                if (!genericTypeExplicit)
                {
                    Debugger.LogError("CommandBinder", obj.name + " event type and command type not explicit.");
                    return;
                }

                command.BindVM(vm);

                sourceEventAction = command.AddListenerToEvent(sourceEventObj, parameter);

                Selectable selectableComponent = component as Selectable;

                if (selectableComponent != null)
                {
                    canExecuteHandler = new Action(delegate
                    {
                        selectableComponent.interactable = command.CanExecute(parameter);
                    });
                    command.AddCanExecuteChangedListener(canExecuteHandler);
                    selectableComponent.interactable = command.CanExecute(parameter);
                }
            }
            private void DoPropertyBind(VMBehaviour vm, GameObject obj)
            {
                if (this.Component == null)
                {
                    Debugger.LogError("DataBinder", obj.name + " component null.");
                    return;
                }

                if (string.IsNullOrEmpty(this.Property))
                {
                    Debugger.LogError("DataBinder", obj.name + " property empty.");
                    return;
                }

                if (componentType == null)
                {
                    componentType       = this.Component.GetType();
                    componentReflection = ReflectionCache.Singleton[componentType];
                }

                PropertyInfo propertyInfo = componentReflection.GetProperty(this.Property);

                if (propertyInfo == null || propertyInfo.GetSetMethod() == null)
                {
                    Debugger.LogError("DataBinder", obj.name + " property null or not support.");
                    return;
                }

                Type propertyType = propertyInfo.PropertyType;

                if (!CheckDataTypeValid(propertyType, obj))
                {
                    return;
                }

                ISetValue propertySetter = SetterWrapper.CreatePropertySetterWrapper(propertyInfo);

                if (propertySetter == null)
                {
                    return;
                }

                // 数据单向绑定
                this.SetValueHandler = delegate(IData source)
                {
                    object value = this.Source.FastGetValue();
                    if (this.Converter != null)
                    {
                        value = this.Converter.Convert(value, propertyType, this.Definer.ConverterParameter, vm);
                    }
                    propertySetter.Set(this.Component, value);

                    // ToggleGroup 特殊处理
                    if (Toggle_Type.IsAssignableFrom(componentType) && this.Property.Equals("isOn"))
                    {
                        Toggle t = this.Component as Toggle;
                        if (t.group != null && t.isOn)
                        {
                            try
                            {
                                t.group.NotifyToggleOn(t);
                            }
                            catch (System.Exception) { }
                        }
                    }
                };
                this.Source.AddValueChangedListener(this.SetValueHandler);
                this.SetValueHandler.Invoke(this.Source);

                // 可交互组件的双向绑定
                if (Toggle_Type.IsAssignableFrom(componentType) && this.Property.Equals("isOn"))
                {
                    this.ValueChangedHandler = new UnityAction <bool>(delegate(bool arg)
                    {
                        if (this.Converter != null)
                        {
                            object value = this.Converter.ConvertBack(arg, this.Source.GetBindDataType(), this.Definer.ConverterParameter, vm);
                            this.Source.FastSetValue(value);
                        }
                        else
                        {
                            (this.Source as BaseData <bool>).Set(arg);
                        }
                    });
                    (this.Component as Toggle).onValueChanged.AddListener((UnityAction <bool>) this.ValueChangedHandler);
                }
                if (Input_Type.IsAssignableFrom(componentType) && this.Property.Equals("text"))
                {
                    this.ValueChangedHandler = new UnityAction <string>(delegate(string arg)
                    {
                        if (this.Converter != null)
                        {
                            object value = this.Converter.ConvertBack(arg, this.Source.GetBindDataType(), this.Definer.ConverterParameter, vm);
                            this.Source.FastSetValue(value);
                        }
                        else
                        {
                            (this.Source as BaseData <string>).Set(arg);
                        }
                    });
                    (this.Component as InputField).onValueChanged.AddListener((UnityAction <string>) this.ValueChangedHandler);
                }
                if (Dropdown_Type.IsAssignableFrom(componentType) && this.Property.Equals("value"))
                {
                    this.ValueChangedHandler = new UnityAction <int>(delegate(int arg)
                    {
                        if (this.Converter != null)
                        {
                            object value = this.Converter.ConvertBack(arg, this.Source.GetBindDataType(), this.Definer.ConverterParameter, vm);
                            this.Source.FastSetValue(value);
                        }
                        else
                        {
                            (this.Source as BaseData <int>).Set(arg);
                        }
                    });
                    (this.Component as Dropdown).onValueChanged.AddListener((UnityAction <int>) this.ValueChangedHandler);
                }
                if (Slider_Type.IsAssignableFrom(componentType) && this.Property.Equals("value"))
                {
                    this.ValueChangedHandler = new UnityAction <float>(delegate(float arg)
                    {
                        if (this.Converter != null)
                        {
                            object value = this.Converter.ConvertBack(arg, this.Source.GetBindDataType(), this.Definer.ConverterParameter, vm);
                            this.Source.FastSetValue(value);
                        }
                        else
                        {
                            (this.Source as BaseData <float>).Set(arg);
                        }
                    });
                    (this.Component as Slider).onValueChanged.AddListener((UnityAction <float>) this.ValueChangedHandler);
                }
            }