Làm thế nào để tự động tạo ra một lớp?


221

Tôi có một lớp trông như thế này:

public class Field
{
    public string FieldName;
    public string FieldType;
}

Và một đối tượng List<Field>có giá trị:

{"EmployeeID","int"},
{"EmployeeName","String"},
{"Designation","String"}

Tôi muốn tạo một lớp trông như thế này:

Class DynamicClass
{
    int EmployeeID,
    String EmployeeName,
    String Designation
}

Có cách nào để làm điều này?

Tôi muốn điều này được tạo ra trong thời gian chạy. Tôi không muốn có tệp CS vật lý nằm trong hệ thống tệp của mình.


4
Bạn có muốn sử dụng lớp đó trong thời gian chạy hoặc chỉ tạo tệp?
Damian Leszczyński - Vash

Tôi muốn điều này được tạo ra trong thời gian chạy. Tôi không muốn một tệp CS vật lý nằm trong hệ thống tệp của mình. Xin lỗi vì đã không đề cập đến điều đó sớm hơn.
ashwnacharya

16
Bạn có thể cho chúng tôi một ý tưởng sơ bộ về những gì bạn dự định làm với lớp học này?
Justin

3
@Justin thực hiện các giao diện giải quyết thời gian chạy, ví dụ.
AgentFire

Người ta có thể nuôi nó đếnSystem.ServiceModel.ChannelFactory<MyDynamicInterface>
Ilya Semenov

Câu trả lời:


297

Có, bạn có thể sử dụng System.Reflection.Emitkhông gian tên cho việc này. Nó không phải là thẳng về phía trước nếu bạn không có kinh nghiệm với nó, nhưng nó chắc chắn là có thể.

Chỉnh sửa: Mã này có thể là thiếu sót, nhưng nó sẽ cung cấp cho bạn ý tưởng chung và hy vọng khởi đầu tốt cho mục tiêu.

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace TypeBuilderNamespace
{
    public static class MyTypeBuilder
    {
        public static void CreateNewObject()
        {
            var myType = CompileResultType();
            var myObject = Activator.CreateInstance(myType);
        }
        public static Type CompileResultType()
        {
            TypeBuilder tb = GetTypeBuilder();
            ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

            // NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
            foreach (var field in yourListOfFields)
                CreateProperty(tb, field.FieldName, field.FieldType);

            Type objectType = tb.CreateType();
            return objectType;
        }

        private static TypeBuilder GetTypeBuilder()
        {
            var typeSignature = "MyDynamicType";
            var an = new AssemblyName(typeSignature);
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
            TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
                    TypeAttributes.Public |
                    TypeAttributes.Class |
                    TypeAttributes.AutoClass |
                    TypeAttributes.AnsiClass |
                    TypeAttributes.BeforeFieldInit |
                    TypeAttributes.AutoLayout,
                    null);
            return tb;
        }

        private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
        {
            FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

            PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
            MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
            ILGenerator getIl = getPropMthdBldr.GetILGenerator();

            getIl.Emit(OpCodes.Ldarg_0);
            getIl.Emit(OpCodes.Ldfld, fieldBuilder);
            getIl.Emit(OpCodes.Ret);

            MethodBuilder setPropMthdBldr =
                tb.DefineMethod("set_" + propertyName,
                  MethodAttributes.Public |
                  MethodAttributes.SpecialName |
                  MethodAttributes.HideBySig,
                  null, new[] { propertyType });

            ILGenerator setIl = setPropMthdBldr.GetILGenerator();
            Label modifyProperty = setIl.DefineLabel();
            Label exitSet = setIl.DefineLabel();

            setIl.MarkLabel(modifyProperty);
            setIl.Emit(OpCodes.Ldarg_0);
            setIl.Emit(OpCodes.Ldarg_1);
            setIl.Emit(OpCodes.Stfld, fieldBuilder);

            setIl.Emit(OpCodes.Nop);
            setIl.MarkLabel(exitSet);
            setIl.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getPropMthdBldr);
            propertyBuilder.SetSetMethod(setPropMthdBldr);
        }
    }
}

2
Tuyệt vời!! Bạn cũng có thể cho tôi biết cách tạo một đối tượng thuộc loại được trả về bởi Phương thức CompileResultType () không?
ashwnacharya

4
Bạn có thể sử dụng System.Activator cho điều đó. Tôi sẽ cập nhật câu trả lời với một ví dụ.
danijels

4
Cũng lưu ý rằng bạn sẽ phải sử dụng sự phản chiếu để kiểm tra, đọc và cập nhật các trường trong loại động của bạn. Nếu bạn muốn intellisense và không có sự phản chiếu, bạn phải có một lớp cơ sở hoặc giao diện tĩnh mà lớp động của bạn kế thừa và có thể được chuyển sang. Trong trường hợp đó, bạn có thể sửa đổi phương thức GetTypeBuilder () và thay đổi lệnh gọi moduleBuilder.DefineType để bao gồm kiểu tĩnh làm tham số cuối cùng (bây giờ là null)
danijels

2
ai đó có thể giải thích cách sử dụng đối tượng sau khi được tạo không
HELP_ME

3
@ormsz sử dụng mã ở trên để tạo lớp, sau đó trong lớp cơ sở, bạn có thể thêm phương thức này: public void SetValue <T> (tên chuỗi, giá trị T) {GetType (). GetProperty (name). SetValue (this, value ); }
stricq

71

Nó sẽ mất một số công việc, nhưng chắc chắn không phải là không thể.

Những gì tôi đã làm là:

  • Tạo nguồn C # trong một chuỗi (không cần phải ghi ra tệp),
  • Chạy nó thông qua Microsoft.CSharp.CSharpCodeProvider(CompileAss lanhFromSource)
  • Tìm Loại đã tạo
  • Và tạo một thể hiện của Loại đó ( Activator.CreateInstance)

Bằng cách này, bạn có thể xử lý mã C # mà bạn đã biết, thay vì phải phát ra MSIL.

Nhưng điều này hoạt động tốt nhất nếu lớp của bạn thực hiện một số giao diện (hoặc được lấy từ một số lớp cơ sở), khác với cách gọi mã (đọc: trình biên dịch) để biết về lớp đó sẽ được tạo khi chạy?


7
Có thể muốn xem cuộc thảo luận này: Reflection-emit-vs-codedom
nawfal

1
@nawfal, ashwnacharya, muốn tạo động khi chạy lớp của nó với các thành viên ban đầu được chứa trong một danh sách. Tôi không nghĩ việc đưa nó vào một tập tin thậm chí được tạo ra trong thời gian chạy sẽ là một giải pháp hiệu suất tốt.
sodjsn26fr

38

Bạn cũng có thể tự động tạo một lớp bằng cách sử dụng DynamicObject .

public class DynamicClass : DynamicObject
{
    private Dictionary<string, KeyValuePair<Type, object>> _fields;

    public DynamicClass(List<Field> fields)
    {
        _fields = new Dictionary<string, KeyValuePair<Type, object>>();
        fields.ForEach(x => _fields.Add(x.FieldName,
            new KeyValuePair<Type, object>(x.FieldType, null)));
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (_fields.ContainsKey(binder.Name))
        {
            var type = _fields[binder.Name].Key;
            if (value.GetType() == type)
            {
                _fields[binder.Name] = new KeyValuePair<Type, object>(type, value);
                return true;
            }
            else throw new Exception("Value " + value + " is not of type " + type.Name);
        }
        return false;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = _fields[binder.Name].Value;
        return true;
    }
}

Tôi lưu trữ tất cả các trường lớp trong một từ điển _fieldscùng với các loại và giá trị của chúng. Cả hai phương pháp là có thể nhận hoặc đặt giá trị cho một số thuộc tính. Bạn phải sử dụng dynamictừ khóa để tạo một thể hiện của lớp này.

Việc sử dụng với ví dụ của bạn:

var fields = new List<Field>() { 
    new Field("EmployeeID", typeof(int)),
    new Field("EmployeeName", typeof(string)),
    new Field("Designation", typeof(string)) 
};

dynamic obj = new DynamicClass(fields);

//set
obj.EmployeeID = 123456;
obj.EmployeeName = "John";
obj.Designation = "Tech Lead";

obj.Age = 25;             //Exception: DynamicClass does not contain a definition for 'Age'
obj.EmployeeName = 666;   //Exception: Value 666 is not of type String

//get
Console.WriteLine(obj.EmployeeID);     //123456
Console.WriteLine(obj.EmployeeName);   //John
Console.WriteLine(obj.Designation);    //Tech Lead

Chỉnh sửa: Và đây là cách lớp của tôi Field:

public class Field
{
    public Field(string name, Type type)
    {
        this.FieldName = name;
        this.FieldType = type;
    }

    public string FieldName;

    public Type FieldType;
}

1
Tôi thích cách tiếp cận này cho đến khi tôi cần khởi tạo các trường với hàm tạo. tức là dynamic obj = new DynamicClass(fields){EmployeeId=123456;EmployeeName = "John"; Designation = "Tech Lead";}nó sẽ thực sự tuyệt vời khi làm điều này
rey_coder

14

Tôi biết tôi mở lại nhiệm vụ cũ này nhưng với c # 4.0 thì nhiệm vụ này hoàn toàn không gây đau đớn.

dynamic expando = new ExpandoObject();
expando.EmployeeID=42;
expando.Designation="unknown";
expando.EmployeeName="curt"

//or more dynamic
AddProperty(expando, "Language", "English");

để biết thêm, hãy xem https://www.oreilly.com/learning/building-c-objects-dynamical


Có, nhưng bạn mất loại an toàn ở đây. Chúng ta có thể làm một cái gì đó tương tự trong khi bảo quản an toàn loại?
khó khăn Câu hỏi

13

Tôi không biết việc sử dụng dự định của các lớp động như vậy, việc tạo mã và biên dịch thời gian chạy có thể được thực hiện, nhưng phải mất một số nỗ lực. Có thể các loại ẩn danh sẽ giúp bạn, đại loại như:

var v = new { EmployeeID = 108, EmployeeName = "John Doe" };

7
Bạn không thể mã cứng tên trường. Anh ấy cung cấp cho họ với của mình Field.FieldName. Phải mã cứng các tên trường đánh bại mục đích. Nếu bạn phải làm điều đó, bạn cũng có thể tạo lớp.
chập chững

9

Bạn muốn xem CodeDOM . Nó cho phép xác định các phần tử mã và biên dịch chúng. Trích dẫn MSDN:

... Biểu đồ đối tượng này có thể được hiển thị dưới dạng mã nguồn bằng cách sử dụng trình tạo mã CodeDOM cho ngôn ngữ lập trình được hỗ trợ. CodeDOM cũng có thể được sử dụng để biên dịch mã nguồn thành một cụm nhị phân.


Tôi muốn điều này được tạo ra trong thời gian chạy. Tôi không muốn một tệp CS vật lý nằm trong hệ thống tệp của mình. Xin lỗi vì đã không đề cập đến điều đó sớm hơn.
ashwnacharya

1
@ashwnacharya: Bạn có thể sử dụng CodeDOM cho cả việc tạo tệp nguồn và biên dịch nó khi chạy!
Hemant

1
Mặc dù vậy, hãy cẩn thận, trình biên dịch CodeDOM có một chuỗi thô và do đó bạn có thể muốn xem xét "các cuộc tấn công chèn mã" tương tự như các cuộc tấn công được sử dụng trong phép tiêm XSS và SQL.
cwap

6

Dựa trên câu trả lời của @ danijels, hãy tự động tạo một lớp trong VB.NET:

Imports System.Reflection
Imports System.Reflection.Emit

Public Class ObjectBuilder

Public Property myType As Object
Public Property myObject As Object

Public Sub New(fields As List(Of Field))
    myType = CompileResultType(fields)
    myObject = Activator.CreateInstance(myType)
End Sub

Public Shared Function CompileResultType(fields As List(Of Field)) As Type
    Dim tb As TypeBuilder = GetTypeBuilder()
    Dim constructor As ConstructorBuilder = tb.DefineDefaultConstructor(MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.RTSpecialName)

    For Each field In fields
        CreateProperty(tb, field.Name, field.Type)
    Next

    Dim objectType As Type = tb.CreateType()
    Return objectType
End Function

Private Shared Function GetTypeBuilder() As TypeBuilder
    Dim typeSignature = "MyDynamicType"
    Dim an = New AssemblyName(typeSignature)
    Dim assemblyBuilder As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run)
    Dim moduleBuilder As ModuleBuilder = assemblyBuilder.DefineDynamicModule("MainModule")
    Dim tb As TypeBuilder = moduleBuilder.DefineType(typeSignature, TypeAttributes.[Public] Or TypeAttributes.[Class] Or TypeAttributes.AutoClass Or TypeAttributes.AnsiClass Or TypeAttributes.BeforeFieldInit Or TypeAttributes.AutoLayout, Nothing)
    Return tb
End Function

Private Shared Sub CreateProperty(tb As TypeBuilder, propertyName As String, propertyType As Type)
    Dim fieldBuilder As FieldBuilder = tb.DefineField("_" & propertyName, propertyType, FieldAttributes.[Private])

    Dim propertyBuilder As PropertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, Nothing)
    Dim getPropMthdBldr As MethodBuilder = tb.DefineMethod("get_" & propertyName, MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.HideBySig, propertyType, Type.EmptyTypes)
    Dim getIl As ILGenerator = getPropMthdBldr.GetILGenerator()

    getIl.Emit(OpCodes.Ldarg_0)
    getIl.Emit(OpCodes.Ldfld, fieldBuilder)
    getIl.Emit(OpCodes.Ret)

    Dim setPropMthdBldr As MethodBuilder = tb.DefineMethod("set_" & propertyName, MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.HideBySig, Nothing, {propertyType})

    Dim setIl As ILGenerator = setPropMthdBldr.GetILGenerator()
    Dim modifyProperty As Label = setIl.DefineLabel()
    Dim exitSet As Label = setIl.DefineLabel()

    setIl.MarkLabel(modifyProperty)
    setIl.Emit(OpCodes.Ldarg_0)
    setIl.Emit(OpCodes.Ldarg_1)
    setIl.Emit(OpCodes.Stfld, fieldBuilder)

    setIl.Emit(OpCodes.Nop)
    setIl.MarkLabel(exitSet)
    setIl.Emit(OpCodes.Ret)

    propertyBuilder.SetGetMethod(getPropMthdBldr)
    propertyBuilder.SetSetMethod(setPropMthdBldr)
End Sub

End Class

6

Bạn cũng có thể tự động tạo một lớp bằng cách sử dụng DynamicExpressions .

Vì 'Dictionary có bộ khởi tạo nhỏ gọn và xử lý các va chạm quan trọng, bạn sẽ muốn làm một cái gì đó như thế này.

  var list = new Dictionary<string, string> {
    {
      "EmployeeID",
      "int"
    }, {
      "EmployeeName",
      "String"
    }, {
      "Birthday",
      "DateTime"
    }
  };

Hoặc bạn có thể muốn sử dụng trình chuyển đổi JSON để xây dựng đối tượng chuỗi được tuần tự hóa của mình thành thứ gì đó có thể quản lý được.

Sau đó, sử dụng System.Linq.Docate;

  IEnumerable<DynamicProperty> props = list.Select(property => new DynamicProperty(property.Key, Type.GetType(property.Value))).ToList();

  Type t = DynamicExpression.CreateClass(props);

Phần còn lại chỉ là sử dụng System.Reflection.

  object obj = Activator.CreateInstance(t);
  t.GetProperty("EmployeeID").SetValue(obj, 34, null);
  t.GetProperty("EmployeeName").SetValue(obj, "Albert", null);
  t.GetProperty("Birthday").SetValue(obj, new DateTime(1976, 3, 14), null);
}  

4

Đối với những người muốn tạo một lớp động chỉ là các thuộc tính (ví dụ POCO) và tạo một danh sách của lớp này. Sử dụng mã được cung cấp sau, điều này sẽ tạo ra một lớp động và tạo một danh sách này.

var properties = new List<DynamicTypeProperty>()
{
    new DynamicTypeProperty("doubleProperty", typeof(double)),
    new DynamicTypeProperty("stringProperty", typeof(string))
};

// create the new type
var dynamicType = DynamicType.CreateDynamicType(properties);
// create a list of the new type
var dynamicList = DynamicType.CreateDynamicList(dynamicType);

// get an action that will add to the list
var addAction = DynamicType.GetAddAction(dynamicList);

// call the action, with an object[] containing parameters in exact order added
addAction.Invoke(new object[] {1.1, "item1"});
addAction.Invoke(new object[] {2.1, "item2"});
addAction.Invoke(new object[] {3.1, "item3"});

Dưới đây là các lớp mà mã trước đó sử dụng.

Lưu ý: Bạn cũng cần tham khảo thư viện Microsoft.CodeAnalysis.CSharp.

       /// <summary>
    /// A property name, and type used to generate a property in the dynamic class.
    /// </summary>
    public class DynamicTypeProperty
    {
        public DynamicTypeProperty(string name, Type type)
        {
            Name = name;
            Type = type;
        }
        public string Name { get; set; }
        public Type Type { get; set; }
    }

   public static class DynamicType
    {
        /// <summary>
        /// Creates a list of the specified type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IEnumerable<object> CreateDynamicList(Type type)
        {
            var listType = typeof(List<>);
            var dynamicListType = listType.MakeGenericType(type);
            return (IEnumerable<object>) Activator.CreateInstance(dynamicListType);
        }

        /// <summary>
        /// creates an action which can be used to add items to the list
        /// </summary>
        /// <param name="listType"></param>
        /// <returns></returns>
        public static Action<object[]> GetAddAction(IEnumerable<object> list)
        {
            var listType = list.GetType();
            var addMethod = listType.GetMethod("Add");
            var itemType = listType.GenericTypeArguments[0];
            var itemProperties = itemType.GetProperties();

            var action = new Action<object[]>((values) =>
            {
                var item = Activator.CreateInstance(itemType);

                for(var i = 0; i < values.Length; i++)
                {
                    itemProperties[i].SetValue(item, values[i]);
                }

                addMethod.Invoke(list, new []{item});
            });

            return action;
        }

        /// <summary>
        /// Creates a type based on the property/type values specified in the properties
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Type CreateDynamicType(IEnumerable<DynamicTypeProperty> properties)
        {
            StringBuilder classCode = new StringBuilder();

            // Generate the class code
            classCode.AppendLine("using System;");
            classCode.AppendLine("namespace Dexih {");
            classCode.AppendLine("public class DynamicClass {");

            foreach (var property in properties)
            {
                classCode.AppendLine($"public {property.Type.Name} {property.Name} {{get; set; }}");
            }
            classCode.AppendLine("}");
            classCode.AppendLine("}");

            var syntaxTree = CSharpSyntaxTree.ParseText(classCode.ToString());

            var references = new MetadataReference[]
            {
                MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
                MetadataReference.CreateFromFile(typeof(DictionaryBase).GetTypeInfo().Assembly.Location)
            };

            var compilation = CSharpCompilation.Create("DynamicClass" + Guid.NewGuid() + ".dll",
                syntaxTrees: new[] {syntaxTree},
                references: references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            using (var ms = new MemoryStream())
            {
                var result = compilation.Emit(ms);

                if (!result.Success)
                {
                    var failures = result.Diagnostics.Where(diagnostic =>
                        diagnostic.IsWarningAsError ||
                        diagnostic.Severity == DiagnosticSeverity.Error);

                    var message = new StringBuilder();

                    foreach (var diagnostic in failures)
                    {
                        message.AppendFormat("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
                    }

                    throw new Exception($"Invalid property definition: {message}.");
                }
                else
                {

                    ms.Seek(0, SeekOrigin.Begin);
                    var assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms);
                    var dynamicType = assembly.GetType("Dexih.DynamicClass");
                    return dynamicType;
                }
            }
        }
    }

Tuyệt vời của mã. Cũng có thể sử dụng AddRange trong danh sách động, để cuối cùng thêm một số bản ghi cùng một lúc?
RickyTad

2

Bạn có thể xem xét bằng cách sử dụng các mô-đun động và các lớp có thể thực hiện công việc. Nhược điểm duy nhất là nó vẫn được tải trong miền ứng dụng. Nhưng với phiên bản .NET framework đang được sử dụng, điều đó có thể thay đổi. .NET 4.0 hỗ trợ các tập hợp động có thể thu và do đó bạn có thể tạo lại các lớp / kiểu một cách linh hoạt.


2

Ồ Cảm ơn bạn vì câu trả lời đó! Tôi đã thêm một số tính năng cho nó để tạo ra trình chuyển đổi "datable to json" mà tôi chia sẻ với bạn.

    Public Shared Sub dt2json(ByVal _dt As DataTable, ByVal _sb As StringBuilder)
    Dim t As System.Type

    Dim oList(_dt.Rows.Count - 1) As Object
    Dim jss As New JavaScriptSerializer()
    Dim i As Integer = 0

    t = CompileResultType(_dt)

    For Each dr As DataRow In _dt.Rows
        Dim o As Object = Activator.CreateInstance(t)

        For Each col As DataColumn In _dt.Columns
            setvalue(o, col.ColumnName, dr.Item(col.ColumnName))
        Next

        oList(i) = o
        i += 1
    Next

    jss = New JavaScriptSerializer()
    jss.Serialize(oList, _sb)


End Sub

Và trong phụ "compersulttype", tôi đã thay đổi:

    For Each column As DataColumn In _dt.Columns
        CreateProperty(tb, column.ColumnName, column.DataType)
    Next


Private Shared Sub setvalue(ByVal _obj As Object, ByVal _propName As String, ByVal _propValue As Object)
    Dim pi As PropertyInfo
    pi = _obj.GetType.GetProperty(_propName)
    If pi IsNot Nothing AndAlso pi.CanWrite Then
        If _propValue IsNot DBNull.Value Then
            pi.SetValue(_obj, _propValue, Nothing)

        Else
            Select Case pi.PropertyType.ToString
                Case "System.String"
                    pi.SetValue(_obj, String.Empty, Nothing)
                Case Else
                    'let the serialiser use javascript "null" value.
            End Select

        End If
    End If

End Sub


-1

Runtime Code Generation with JVM and CLR - Peter Sestoft

Làm việc cho những người thực sự quan tâm đến loại lập trình này.

Lời khuyên của tôi dành cho Bạn là nếu Bạn khai báo điều gì đó hãy cố gắng tránh chuỗi, vì vậy nếu Bạn có Trường lớp, tốt hơn là sử dụng lớp System.Type để lưu trữ loại trường hơn là một chuỗi. Và vì lợi ích của các giải pháp tốt nhất thay vì tạo các lớp mới, hãy thử sử dụng các lớp đã được tạo FiledInfo thay vì tạo mới.


2
Liên kết đã chết: -1
Glenn Slayden

Glenn: một googling nhanh chóng tiết lộ một liên kết hoạt động: pdfs.semanticscholar.org / 32a / Từ
Andreas Pardeike

@AndreasPardeike Cảm ơn, tôi đã sửa nó trong bài viết.
Glenn Slayden
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.