ASP.NET控件为什么无法使用结构?

一次在开发一个3D“点”用户Web控件的时候用了结构体,结果碰到了小问题,先给出代码(核心部分):

[C#]

namespace CSharp
{
    namespace CSharp
    {
        /// <summary>
        /// 三维坐标的Struct结构
        /// </summary>
        [TypeConverter(typeof(MyConvertor))]
        public struct DPoint
        {
            private double _x;
            private double _y;
            private double _z;
            [NotifyParentProperty(true)]
            public double Z
            {
                get { return _z; }
                set { _z = value; }
            }
            [NotifyParentProperty(true)]
            public double Y
            {
                get { return _y; }
                set { _y = value; }
            }
            [NotifyParentProperty(true)]
            public double X
            {
                get { return _x; }
                set { _x = value; }
            }

            public DPoint(double x, double y, double z)
            {
                _x = x;
                _y = y;
                _z = z;
            }
            public static explicit operator DPoint(string s)
            {
                string[] values = s.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                if (values.Length == 3)
                {
                    double d = Convert.ToDouble(values[0]);
                    double d2 = Convert.ToDouble(values[1]);
                    double d3 = Convert.ToDouble(values[2]);
                    return new DPoint(d, d2, d3);
                }
                throw new Exception("输入的维数太多或者太少!");
            }
        }

        /// <summary>
        /// 自定义从String转化成DPoint类型 
        /// </summary>
        public class MyConvertor : ExpandableObjectConverter
        {
            /// <summary>
            ///  判断只能string类型作为转换对象,即该标签只加载String上有效
            /// </summary>
            /// <param name="context"></param>
            /// <param name="sourceType"></param>
            /// <returns></returns>
            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                return typeof(string) == sourceType.GetType();
            }

            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                return (DPoint)value.ToString();
            }

            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
            {
                if (value is DPoint)
                {
                    DPoint dp = (DPoint)value;
                    return string.Format("{0},{1},{2}", dp.X, dp.Y, dp.Z);
                }
                throw new Exception("无法转化成标准的String格式!");
            }
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                return destinationType.GetType() == typeof(string);
            }

            public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
            {
                return new DPoint((double)propertyValues["X"], (double)propertyValues["Y"],(double)propertyValues["Z"]);
            }
         }
        /// <summary>
        /// 三位数组自定义类
        /// </summary>
        [ParseChildren(true), PersistChildren(false)]
        public class MyDPoint : WebControl
        {
            DPoint dp = new DPoint();

            [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
            [PersistenceMode(PersistenceMode.InnerProperty)]
            [NotifyParentProperty(true)]
            public DPoint DPoint
            {
                get
                {
                    return dp;
                }
                set
                {
                    dp = value;
                }
            }

            protected override void Render(HtmlTextWriter writer)
            {
                writer.Write(string.Format("[{0},{1},{2}]", DPoint.X, DPoint.Y, DPoint.Z));
            }
        }
    }
}

[VB.NET]

results

.


 

copy to clipboard | view source | convert again

Namespace CSharp
    Namespace CSharp
        ''' <summary>
        ''' 三维坐标的Struct结构
        ''' </summary>
        <TypeConverter(GetType(MyConvertor))> _
        Public Structure DPoint
            Private _x As Double
            Private _y As Double
            Private _z As Double
            <NotifyParentProperty(True)> _
            Public Property Z() As Double
                Get
                    Return _z
                End Get
                Set
                    _z = value
                End Set
            End Property
            <NotifyParentProperty(True)> _
            Public Property Y() As Double
                Get
                    Return _y
                End Get
                Set
                    _y = value
                End Set
            End Property
            <NotifyParentProperty(True)> _
            Public Property X() As Double
                Get
                    Return _x
                End Get
                Set
                    _x = value
                End Set
            End Property

            Public Sub New(x As Double, y As Double, z As Double)
                _x = x
                _y = y
                _z = z
            End Sub
            Public Shared Narrowing Operator CType(s As String) As DPoint
                Dim values As String() = s.Split(New String() {","}, StringSplitOptions.RemoveEmptyEntries)
                If values.Length = 3 Then
                    Dim d As Double = Convert.ToDouble(values(0))
                    Dim d2 As Double = Convert.ToDouble(values(1))
                    Dim d3 As Double = Convert.ToDouble(values(2))
                    Return New DPoint(d, d2, d3)
                End If
                Throw New Exception("输入的维数太多或者太少!")
            End Operator
        End Structure

        ''' <summary>
        ''' 自定义从String转化成DPoint类型 
        ''' </summary>
        Public Class MyConvertor
            Inherits ExpandableObjectConverter
            ''' <summary>
            '''  判断只能string类型作为转换对象,即该标签只加载String上有效
            ''' </summary>
            ''' <param name="context"></param>
            ''' <param name="sourceType"></param>
            ''' <returns></returns>
            Public Overrides Function CanConvertFrom(context As ITypeDescriptorContext, sourceType As Type) As Boolean
                Return GetType(String) = sourceType.[GetType]()
            End Function

            Public Overrides Function ConvertFrom(context As ITypeDescriptorContext, culture As System.Globalization.CultureInfo, value As Object) As Object
                Return CType(value.ToString(), DPoint)
            End Function

            Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As System.Globalization.CultureInfo, value As Object, destinationType As Type) As Object
                If TypeOf value Is DPoint Then
                    Dim dp As DPoint = CType(value, DPoint)
                    Return String.Format("{0},{1},{2}", dp.X, dp.Y, dp.Z)
                End If
                Throw New Exception("无法转化成标准的String格式!")
            End Function
            Public Overrides Function CanConvertTo(context As ITypeDescriptorContext, destinationType As Type) As Boolean
                Return destinationType.[GetType]() = GetType(String)
            End Function

            Public Overrides Function CreateInstance(context As ITypeDescriptorContext, propertyValues As System.Collections.IDictionary) As Object
                Return New DPoint(CDbl(propertyValues("X")), CDbl(propertyValues("Y")), CDbl(propertyValues("Z")))
            End Function
        End Class
        ''' <summary>
        ''' 三位数组自定义类
        ''' </summary>
        <ParseChildren(True), PersistChildren(False)> _
        Public Class MyDPoint
            Inherits WebControl
            Private dp As New DPoint()

            <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
            <PersistenceMode(PersistenceMode.InnerProperty)> _
            <NotifyParentProperty(True)> _
            Public Property DPoint() As DPoint
                Get
                    Return dp
                End Get
                Set
                    dp = value
                End Set
            End Property

            Protected Overrides Sub Render(writer As HtmlTextWriter)
                writer.Write(String.Format("[{0},{1},{2}]", DPoint.X, DPoint.Y, DPoint.Z))
            End Sub
        End Class
    End Namespace
End Namespace

编译整个程序之后直接从工具栏把该控件拖拽到页面上,一切OK;随后接着在设计器(属性面板)中修改X,Y,Z的内容——悲剧发生了!

这是为啥?Why?不知道哎……不能用结构?结构没有序列化?结构的构造函数默认是无参的,结构体没初始化一次就丢弃原来的那个……?种种胡乱猜测萦绕在我心头……乱乱乱,烦烦烦!

正当我绝望之际,无疑习惯性地在自定义的MyConvertor中敲入“public override”一行字符时候,一个东西映入我眼帘:

去查询MSDN文档,发现该方法是虚方法,作用是“返回此对象是否支持可以从列表中选取的标准值集”。重写它设置成true就可以啦!

现在思考以下——为什么Class不需要重写它(默认返回false)也可以,但是struct就一定需要呢?

【分析】

如果你使用结构,那么编译之后aspx代码是这个样子:

<cc1:MyDPoint runat="server" DPoint-X="5" DPoint-Y="5" DPoint-Z="5"></cc2:MyDPoint>

这个代码实质类似:
[C#]

MyDPoint.DPoint.X = 5;

[VB.NET]

MyDPoint.DPoint.X = 5

但是这个请注意:因为DPoint是MyDPoint的一个属性(是结构类型!),因此这意味着你无法改变MyDPoint.DPoint真实值(你可以想想,如果MyDPoint.DPoint赋值给其它DPoint,并且视图改变X,Y或者是Z的数值,结果是无法影响到MyDPoint.DPoint的)。
值得注意的是——VS的编译器也是无法通过以上语句的编译的。

原文地址:https://www.cnblogs.com/ServiceboyNew/p/2782219.html