PC&웹/VS프로그래밍

C#, Object 값 형식을 보관하는 일반화된 클래스

Simulz™ 2022. 11. 8. 16:48
반응형

DevExpress의 컨트롤을 사용하면 값을 읽고 쓸 수 있는 EditValue 항목이 있다.

그런데 Object 형식이다 보니, 다른 곳에 값을 전달할 때 형변환을 해야하는 귀차니즘이 있다.
DataRow Column 값도 마찬가지.

그래서 자동으로 형변환을 해주는 클래스를 만들었다. 처음에는 변수 선언을 해야 하니 반자동...
(개인용이므로 참고만 할 것)

 

코드

using System;
using System.Collections.Generic;
using System.Data;

namespace Data
{
    /*
     * Update: 2023-05-18
     */

    /// <summary>
    /// 레코드의 데이터와 컬럼 정보를 포함합니다.
    /// </summary>
    /// <typeparam name="T">데이터 형식</typeparam>
    public record Value<T> : IComparable<Value<T>>
    {
        public Value(string name)
        {
            Name = name;
        }
        /// <summary>
        /// 컬럼명. Column Name
        /// </summary>
        public string Name { get; private set; }
        /// <summary>
        /// 파라미터 이름을 가져옵니다. Parameter Name
        /// </summary>
        public string ParamName { get => $"@{Name}"; }
        /// <summary>
        /// 데이터 값. for TypedValue
        /// </summary>
        protected T fEditValue;
        /// <summary>
        /// 객체 형식의 값을 저장하거나 가져옵니다. Object Value
        /// </summary>
        public virtual object EditValue
        {
            get => fEditValue;
            set
            {
                if (value == DBNull.Value)
                {
                    fEditValue = default;
                }
                else if (CSType.IsGenericType && CSType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                {
                    if (value == null)
                    {
                        fEditValue = default(T);
                    }
                    else
                    {
                        var parseData = value;
                        if (IsRequireParse)
                        {
                            if (CallbackParser is null) throw new NoNullAllowedException("CallbackParser is null");
                            parseData = CallbackParser(value);
                        }
                        fEditValue = (T)Convert.ChangeType(parseData, Nullable.GetUnderlyingType(CSType));
                    }
                }
                else
                {
                    var parseData = value;
                    if (IsRequireParse)
                    {
                        if (CallbackParser is null) throw new NoNullAllowedException("CallbackParser is null");
                        parseData = CallbackParser(value);
                    }
                    fEditValue = (T)Convert.ChangeType(parseData, CSType);
                }
                if (!AllowDBNull && fEditValue == null)
                {
                    if (CSType == typeof(string))
                        fEditValue = (T)(object)string.Empty;
                    else if (typeof(T).IsValueType)
                        throw new NoNullAllowedException("Null 값을 허용하지 않습니다.");
                }
            }
        }
        /// <summary>
        /// 형식화된 값을 저장하거나 가져옵니다. Typed Value
        /// </summary>
        public virtual T TypedValue { get => fEditValue; set => fEditValue = value; }
        /// <summary>
        /// DB 형식의 값을 가져옵니다. DB Typed Value
        /// </summary>
        public object DbValue { get => (object)fEditValue ?? DBNull.Value; }
        /// <summary>
        /// 데이터 길이. Data Length
        /// </summary>
        public int DbSize { get; set; }
        /// <summary>
        /// C# 데이터 형식을 가져옵니다. C# Type
        /// </summary>
        public Type CSType { get => typeof(T); }
        /// <summary>
        /// DB 데이터 형식을 저장하거나 가져옵니다. DB Type
        /// </summary>
        public DbType DbType { get; set; }
        /// <summary>
        /// Null 값 허용 여부. Allow DB Nullable
        /// </summary>
        public bool AllowDBNull { get; set; }
        /// <summary>
        /// 테이블 형식. DataColumn
        /// </summary>
        public DataColumn DataColumn { get; set; }
        /// <summary>
        /// 설명. Description
        /// </summary>
        public string Description { get; set; }

        public bool IsRequireParse { get; set; }
        public Func<object, T> CallbackParser { get; set; }

        int IComparable<Value<T>>.CompareTo(Value<T> other)
        {
            if (other == null) throw new ArgumentNullException("other");

            if (CSType == other.CSType)
            {
                if (EqualityComparer<T>.Default.Equals(fEditValue, other.fEditValue))
                {
                    return 0;
                }
                else
                {
                    return -1;
                }
            }
            else
            {
                return -1;
            }
        }
    }
}

잡다한게 많은 이유는 DataTable, Database에 사용해야 해서...

 

사용

클래스 내에서 선언
public Value<decimal> DEC { get; set; }

메소드 내에서 정의
DEC = new("가격") { DbType = DbType.Decimal, EditValue = value };


사용
값을 연산할 때
editDEC.EditValue = Math.Abs(DEC.TypedValue);

DataRow에서 인덱싱할 때
editDEC.EditValue = dataRow[DEC.Name];

개체.TypedValue를 사용하면 형변환된 값을 가져올 수 있다. 위의 예에서는 Math.Abs(decimal 값)이 된다.

DataRow에서 컬럼 인덱싱할 때, 문자열은 리팩터리가 안 되고 인텔리센스가 안 되니 오류 위험이 많은데, 위처럼 개체.Name을 사용하면 알아서 문자열 값을 적용한다.

 

OleDb, FireDb 등 각 데이터베이스 유형별로 만들어 사용한다. 데이터베이스 유형은 일반화할 필요가 없다.

반응형