IT

모든 nullable에 대한 C # 제네릭 형식 제약 조건

lottoking 2020. 8. 22. 10:00
반응형

모든 nullable에 대한 C # 제네릭 형식 제약 조건


그래서이 수업이 있습니다 :

public class Foo<T> where T : ???
{
    private T item;

    public bool IsNull()
    {
        return item == null;
    }

}

이제 모든 것을 사용할 수있는 유형의 변수로 사용할 수있는 유형 제약 조건을 찾고 있습니다 null. 즉, 모든 참조 유형과 모든 Nullable( T?) 유형을 의미합니다.

Foo<String> ... = ...
Foo<int?> ... = ...

가능해야합니다.

사용 class유형 제약은 나를 참조 유형을 사용할 수 있습니다.

추가 정보 : 파이프 및 필터 애플리케이션을 작성 중이며 파이프 null라인으로 전달되는 마지막 항목 으로 참조 를 사용하여 모든 필터가 멋지게 종료되고 정리 등을 수행 할 수 있도록 하고 싶습니다 .


타임 검사를하지 않고 Foo의 생성자에서 작동 검사를 수행하려는 경우 유형이 참조 또는 nullable 유형이 아닌지 확인하고 예외를 throw 할 수 있습니다.

작동 확인 만 허용하지 않을 수 있습니다.

public class Foo<T>
{
    private T item;

    public Foo()
    {
        var type = typeof(T);

        if (Nullable.GetUnderlyingType(type) != null)
            return;

        if (type.IsClass)
            return;

        throw new InvalidOperationException("Type is not nullable or reference type.");
    }

    public bool IsNull()
    {
        return item == null;
    }
}

그런 다음 다음 코드가 마지막 코드 ( foo3)는 생성자에서 예외를 발생시킵니다.

var foo1 = new Foo<int?>();
Console.WriteLine(foo1.IsNull());

var foo2 = new Foo<string>();
Console.WriteLine(foo2.IsNull());

var foo3= new Foo<int>();  // THROWS
Console.WriteLine(foo3.IsNull());

제네릭 에서 OR 와 동등한 것을 구현하는 방법을 모르겠습니다 . 그러나 nullable 형식에는 null을 생성하는 구조에 0 값을 만들기 위해 기본 키워드 를 사용하도록 제안 할 수 있습니다 .

public class Foo<T>
{
    private T item;

    public bool IsNullOrDefault()
    {
        return Equals(item, default(T));
    }
}

Nullable 버전을 구현할 수도 있습니다.

class MyNullable<T> where T : struct
{
    public T Value { get; set; }

    public static implicit operator T(MyNullable<T> value)
    {
        return value != null ? value.Value : default(T);
    }

    public static implicit operator MyNullable<T>(T value)
    {
        return new MyNullable<T> { Value = value };
    }
}

class Foo<T> where T : class
{
    public T Item { get; set; }

    public bool IsNull()
    {
        return Item == null;
    }
}

예 :

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(new Foo<MyNullable<int>>().IsNull()); // true
        Console.WriteLine(new Foo<MyNullable<int>> {Item = 3}.IsNull()); // false
        Console.WriteLine(new Foo<object>().IsNull()); // true
        Console.WriteLine(new Foo<object> {Item = new object()}.IsNull()); // false

        var foo5 = new Foo<MyNullable<int>>();
        int integer = foo5.Item;
        Console.WriteLine(integer); // 0

        var foo6 = new Foo<MyNullable<double>>();
        double real = foo6.Item;
        Console.WriteLine(real); // 0

        var foo7 = new Foo<MyNullable<double>>();
        foo7.Item = null;
        Console.WriteLine(foo7.Item); // 0
        Console.WriteLine(foo7.IsNull()); // true
        foo7.Item = 3.5;
        Console.WriteLine(foo7.Item); // 3.5
        Console.WriteLine(foo7.IsNull()); // false

        // var foo5 = new Foo<int>(); // Not compile
    }
}


"nullable"(참조 형식 또는 Nullables)을 사용할 수있는 정적 메서드를 원하는 더 간단한 경우에 대해 논의이 문제에 직면하여 만족스러운 솔루션 없이이 질문을 충족합니다. 그래서 나는 두 개의 오버로드 된 방법, 즉 a T및 제약 조건이있는 where T : class다른 하나는 a T?where T : struct.

그런 다음 해당 솔루션을이 문제에 적용하여 생성하여 비공개 (또는 보호)하고 정적 팩토리 방법을 사용하여 타임에 확인할 수있는 솔루션을 만들 수도있는 것이 깨달았습니다.

    //this class is to avoid having to supply generic type arguments 
    //to the static factory call (see CA1000)
    public static class Foo
    {
        public static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return Foo<TFoo>.Create(value);
        }

        public static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return Foo<TFoo?>.Create(value);
        }
    }

    public class Foo<T>
    {
        private T item;

        private Foo(T value)
        {
            item = value;
        }

        public bool IsNull()
        {
            return item == null;
        }

        internal static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return new Foo<TFoo>(value);
        }

        internal static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return new Foo<TFoo?>(value);
        }
    }

이제 다음과 같이 사용할 수 있습니다.

        var foo1 = new Foo<int>(1); //does not compile
        var foo2 = Foo.Create(2); //does not compile
        var foo3 = Foo.Create(""); //compiles
        var foo4 = Foo.Create(new object()); //compiles
        var foo5 = Foo.Create((int?)5); //compiles

매개 변수가없는 생성자를 원하면 오버로딩이 좋지는 않지만 다음과 같은 작업을 수행 할 수 있습니다.

    public static class Foo
    {
        public static Foo<TFoo> Create<TFoo>()
            where TFoo : class
        {
            return Foo<TFoo>.Create<TFoo>();
        }

        public static Foo<TFoo?> CreateNullable<TFoo>()
            where TFoo : struct
        {
            return Foo<TFoo?>.CreateNullable<TFoo>();
        }
    }

    public class Foo<T>
    {
        private T item;

        private Foo()
        {
        }

        public bool IsNull()
        {
            return item == null;
        }

        internal static Foo<TFoo> Create<TFoo>()
            where TFoo : class
        {
            return new Foo<TFoo>();
        }

        internal static Foo<TFoo?> CreateNullable<TFoo>()
            where TFoo : struct
        {
            return new Foo<TFoo?>();
        }
    }

다음과 같이 사용하십시오.

        var foo1 = new Foo<int>(); //does not compile
        var foo2 = Foo.Create<int>(); //does not compile
        var foo3 = Foo.Create<string>(); //compiles
        var foo4 = Foo.Create<object>(); //compiles
        var foo5 = Foo.CreateNullable<int>(); //compiles

이 솔루션에는 몇 가지 단점이 있습니다. 하나는 객체를 구성하는 데 'new'를 사용하는 것을 선호 할 수 있다는 것입니다. 다른 하나는 Foo<T>다음과 같은 유형 제약 조건에 대한 일반 유형 인수 로 사용할 수 없다는 것 where TFoo: new()입니다. 마지막으로 여기에 필요한 추가 코드는 특히 여러 개의 오버로드 된 생성자가 필요한 경우 증가합니다.


언급했듯이 컴파일 시간을 확인할 수 없습니다. .NET의 일반 제약 조건은 심각하게 부족하며 대부분의 시나리오를 지원하지 않습니다.

그러나 나는 이것이 런타임 검사를위한 더 나은 솔루션이라고 생각합니다. 둘 다 상수이기 때문에 JIT 컴파일 시간에 최적화 할 수 있습니다.

public class SomeClass<T>
{
    public SomeClass()
    {
        // JIT-compile time check, so it doesn't even have to evaluate.
        if (default(T) != null)
            throw new InvalidOperationException("SomeClass<T> requires T to be a nullable type.");

        T variable;
        // This still won't compile
        // variable = null;
        // but because you know it's a nullable type, this works just fine
        variable = default(T);
    }
}

이러한 유형 제약은 불가능합니다. 형식 제약 에 대한 문서에 따르면 nullable 형식과 참조 형식을 모두 캡처하는 제약 조건이 없습니다. 제약은 결합으로 만 결합 될 수 있기 때문에 이러한 제약을 조합으로 만들 수있는 방법이 없습니다.

그러나 항상 == null을 확인할 수 있으므로 필요에 따라 unconstraint 형식 매개 변수로 대체 할 수 있습니다. 유형이 값 유형이면 검사는 항상 거짓으로 평가됩니다. 그런 다음 의미 체계가 적합한 한 R # 경고 "값 유형을 null과 비교할 수 있음"이 표시 될 수 있습니다. 이는 중요하지 않습니다.

대안은 다음을 사용할 수 있습니다.

object.Equals(value, default(T))

null 검사 대신 default (T) 여기서 T : class는 항상 null입니다. 그러나 이는 nullable이 아닌 값이 명시 적으로 설정되지 않았거나 기본값으로 설정된 경우 날씨를 구분할 수 없음을 의미합니다.


나는 사용한다

public class Foo<T> where T: struct
{
    private T? item;
}

    public class Foo<T>
    {
        private T item;

        public Foo(T item)
        {
            this.item = item;
        }

        public bool IsNull()
        {
            return object.Equals(item, null);
        }
    }

    var fooStruct = new Foo<int?>(3);
        var b = fooStruct.IsNull();

        var fooStruct1 = new Foo<int>(3);
        b = fooStruct1.IsNull();

        var fooStruct2 = new Foo<int?>(null);
        b = fooStruct2.IsNull();

        var fooStruct3 = new Foo<string>("qqq");
        b = fooStruct3.IsNull();

        var fooStruct4 = new Foo<string>(null);
        b = fooStruct4.IsNull();

참고 URL : https://stackoverflow.com/questions/19831157/c-sharp-generic-type-constraint-for-everything-nullable

반응형