Подтвердить что ты не робот

Как получить строчное имя объекта, даже если ноль, в С#

У меня есть метод С#

private static string TypeNameLower(object o)
{
   return o.GetType().Name.ToLower();
}

чтобы дать мне имя типа нижнего регистра входного объекта.

Но если ввод представляет собой строку, установленную в значение null, или значение nullable int установлено в null, этот метод, конечно, терпит неудачу.

Как получить имя типа в этой ситуации?

4b9b3361

Ответ 1

Джефф прав. Это все равно, что спросить, какой торт был бы в пустой коробке без этикетки.

В качестве альтернативы ответу на Фортран вы также можете сделать следующее:

string TypeNameLower<T>(T obj) {
   return typeof(T).Name.ToLower(CultureInfo.InvariantCulture);
}

string TypeNameLower(object obj) {
   if (obj != null) { return obj.GetType().Name.ToLower(CultureInfo.InvariantCulture); }
   else { return null; }
}

string s = null;
TypeNameLower(s); // goes to the generic version

Таким образом, С# выберет универсальный во время компиляции, если он знает достаточно о типе, который вы передаете.

Ответ 2

Я думал, что отправлю свой ответ, хотя этот вопрос старый, потому что, на мой взгляд, принятый ответ неверен. Этот ответ был довольно креативным, поэтому я не собираюсь его сбивать. И для всего, что я знаю, это может быть то, что действительно хотел ОП.

Но, как вы увидите из моих примеров ниже, я думаю, что в почти всех случаях идея использования общей функции, описанной в принятом ответе, либо (A) не нужна, либо ( B) неправильно. Я скопировал общую функцию, о которой я говорю из принятого ответа, и вставлял ее ниже для справки:

string TypeNameLower<T>(T obj) {
    return typeof(T).Name.ToLower();
}

Теперь рассмотрим некоторые способы использования этой функции:

Примеры, когда общая функция не нужна:

var s = "hello";
var t = TypeNameLower(s);

//or
foreach(char c in "banana")
    WriteLine(TypeNameLower(c));

//or
foreach(MyCustomStruct x in listOfMyCustomStruct)
    WriteLine(TypeNameLower(x));

В этих примерах функция работает, т.е. возвращает правильные значения, которые являются "строками", "char" и "mycustomstruct" соответственно. Однако во всех случаях, подобных этим (т.е. Когда общая функция действительно возвращает правильный тип), компилятор заранее знает, что такое определенный тип переменной, и, конечно же, программист (если только они не запутались их имена переменных). Таким образом, функция совершенно не нужна, и программист тоже может это сделать:

var s = "hello";
var t = "string";

//or
foreach(char c in "banana")
    WriteLine("char");

//or
foreach(MyCustomStruct x in listOfMyCustomStruct)
    WriteLine("mycustomstruct");

Сначала это может показаться наивным, но подумайте об этом некоторое время... для этого может потребоваться некоторое время, чтобы он действительно утонул... Попробуйте найти ЛЮБОЙ сценарий, в котором использование общей функции обеспечивает точную информацию на Время выполнения, которое еще не известно (и, следовательно, может быть автоматически сгенерировано компилятором или утилитами генерации кода, такими как шаблоны T4) в время компиляции.

Теперь точка предыдущего набора примеров заключалась в том, чтобы продемонстрировать незначительную досаду с общей функцией - что она не нужна в каждом случае, когда она возвращает правильный результат. Но что более важно, взгляните на приведенные ниже примеры. Они показывают, что в любом другом случае результат общей функции фактически неверен, если вы ожидаете, что функция вернет имя истинного типа времени выполнения объекта. Функция фактически только гарантированно возвращает имя типа, которому может присваиваться истинное значение, которое может быть классом предка, интерфейсом или самим объектом.

Примеры, когда общая функция не соответствует

Stream ms = new MemoryStream();
IEnumerable str = "Hello";
IComparable i = 23;
object j = 1;

TypeNameLower(ms); //returns "stream" instead of "memorystream"
TypeNameLower(str); //returns "ienumerable" instead of "string"
TypeNameLower(i); //returns "icomparable" instead of "int32"
TypeNameLower(j); //returns "object" instead of "int32"
TypeNameLower<object>(true); //returns "object" instead of "bool"

Во всех случаях результаты совершенно неправильны, как вы можете видеть. Теперь я признаю, что последние две строки были немного надуманны, чтобы продемонстрировать эту точку (не говоря уже о том, что TypeNameLower(j) фактически будет скомпилирован для использования не-универсальной версии функции, которая также является частью принятого ответа, - но вы получаете идею...)

Проблема состоит в том, что функция фактически игнорирует тип передаваемого объекта и использует только информацию об обобщенном параметре (время компиляции) для возврата значения.

Лучшая реализация будет следующей:

string TypeNameLower<T>(T obj) {
    Type t;
    if (obj == null)
        t = typeof(T);
    else 
        t = obj.GetType();
    return t.Name.ToLower();
}

Теперь функция возвращает имя истинного типа времени выполнения всякий раз, когда объект не является нулевым, и по умолчанию используется тип времени компиляции/определенного типа, когда тип null.

Важно отметить, что эту функцию можно использовать БЕЗ универсальной версии! Результатом будет то, что функция никогда не вернется null. Наиболее общим возвращаемым значением будет "объект", например:

 object x = null; 
 string s = null;
 byte[] b = null;
 MyClass m = null;
 TypeNameLower(x); // returns "object"
 TypeNameLower(s); // returns "string"
 TypeNameLower(b); // returns "byte[]"
 TypeNameLower(m); // returns "myclass"

Обратите внимание, что на самом деле это более согласованный с заданной целью функции, как запрошено OP. То есть, если OP действительно хочет узнать, что такое имя типа объекта, если оно не было null, то возврат null никогда не будет подходящим ответом, так как null НЕ является именем любого типа и typeof (null) не определен.

Каждая переменная в С# опускается от System.Object, поэтому по определению, если значение не было null, тогда это было бы Object, и это во многих случаях самое большее, что можно определить относительно нулевой ссылки во время выполнения.

Ответ 3

// Uses the compiler type inference mechanisms for generics to find out the type
// 'self' was declared with in the current scope.
static public Type GetDeclaredType<TSelf>(TSelf self)
{
    return typeof(TSelf);
}

void Main()
{
    // ...

    Foo bar;
    bar = null;

    Type myType = GetDeclaredType(bar);
    Console.Write(myType.Name);
}

Печать

Foo

Я разместил это также по аналогичной теме, я надеюсь, что это будет полезно для вас.; -)

Ответ 4

if (o == null) return "null";
else return o.GetType().Name.ToLower();

простое решение для простой задачи: -p

Ответ 5

Как упоминают другие, вы не можете. Это на самом деле хорошо известная проблема с языками, которые допускают чисто нулевые ссылки на объекты. Один из способов обойти это - использовать шаблон "Null Object". Основная идея заключается в том, что вместо использования null для пустых ссылок вы назначаете ему экземпляр объекта "ничего не делать". Например:

public class Circle
{
    public virtual float Radius { get; set; }

    public Circle(float radius)
    {
        Radius = radius;
    }
}

public class NullCircle : Circle
{
    public override float Radius 
    { 
        get { return float.NaN; }
        set { }
    }

    public NullCircle() { }
}

Затем вы можете передать экземпляр NullCircle вместо null, и вы сможете проверить его тип, как в вашем коде.

Ответ 6

Насколько я знаю, вы не можете. Null указывает на отсутствие значения и не отличается для разных типов.

Ответ 7

Нет понятия, что пустая строка отличается от нулевого массива, отличная от нуля. Изнутри вашей функции вы не можете определить имя типа.

В частности, экземпляр ссылочного класса (внутри) включает в себя "указатель" на информацию о типе объекта. Когда ввод является нулевым, такой указатель отсутствует, поэтому информация типа не существует.

Ответ 8

Просто расширяя ответ @Josh Эйнштейна.

Ниже приведены два метода расширения, чтобы получить тип переменной, даже если в настоящее время она установлена ​​в значение null.

    /// <summary>
    /// Gets an object type even if it is null.
    /// </summary>
    /// <typeparam name="T">The type of the object.</typeparam>
    /// <param name="that">The object being extended.</param>
    /// <returns>The objects type.</returns>
    public static Type GetTheType<T>(this T that)
    {
        return typeof(T);
    }

    /// <summary>
    /// Gets an object type even if it is null.
    /// </summary>
    /// <param name="that">The object being extended.</param>
    /// <returns>The objects type.</returns>
    public static Type GetTheType(this object that)
    {
        if (that != null)
        {
            return that.GetType();
        }

        return null;
    }

Кроме того, здесь приведены два простых модульных теста для тестирования методов расширения.

    /// <summary>
    /// Tests to make sure that the correct type is return.
    /// </summary>
    [Test(Description = "Tests to make sure that the correct type is return.")]
    public void Test_GetTheType()
    {
        var value = string.Empty;

        var theType = value.GetTheType();

        Assert.That(theType, Is.SameAs(typeof(string)));
    }

    /// <summary>
    /// Tests to make sure that the correct type is returned even if the value is null.
    /// </summary>
    [Test(Description = "Tests to make sure that the correct type is returned even if the value is null.")]
    public void Test_GetTheType_ReturnsTypeEvenIfValueIsNull()
    {
        string value = null;

        var theType = value.GetTheType();

        Assert.That(theType, Is.SameAs(typeof(string)));
    }

Edit Извините, я забыл упомянуть, что мне нужна такая же функция для проекта, в котором я сейчас. Все кредиты все равно должны пойти на @Josh Эйнштейна: D

Ответ 9

Очень сложно, что С# не допускает такого определения. И не смущает вопрос, какой торт вы имели бы в пустой коробке - объект состоит из двух независимых компонентов - "воплощения" объекта и информации о классе, который использовался для создания объекта. Тот факт, что эта информация не может быть легко доступна, - это упущение со стороны разработчиков С#.

Все, что вы можете сделать путем определения, - это довольно искажающий метод:

void Method(object obj)
{
if(obj is int)
{
//obj is of the int type
}
else if(obj is SomeComplexType)
{
//obj is of the SomeComplexType type
}
}

Итак, вы можете видеть, что даже если объект имеет значение null, его информация о типе, тем не менее, перемещается вместе с объектом, он не потерян, вы просто не можете его легко получить. Но это, мягко говоря, неудобно.

Ответ 10

Если у вас есть объект сам по себе (скажем, как входной параметр для метода с объектом типа), без определения или общего типа, нет способа найти этот тип. Причина проста: вы не можете отправить сообщение (вызвать любой метод) на объект, чтобы спросить о типе.

В некоторых ответах могут возникнуть некоторые другие способы обхода проблемы, например, использование общих типов. В этом случае вы не запрашиваете объект Null, вы запрашиваете типовой тип для своего типа.

Ответ 11

Рассмотрим этот код:

    public class MyClass1{}
    public class MyClass2{}

    public static void Test1()
    {
        MyClass1 one = null;
        MyClass2 two = (MyClass2) (object) one;

        one = new MyClass1();
        //invalid cast exception
        two = (MyClass2)(object) one;
    }

Тип выполнения нулевого экземпляра типа object, по крайней мере, с точки зрения безопасности типа.