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的编译器也是无法通过以上语句的编译的。