Если вы не инициализировали ссылочный тип и хотите установить или прочитать одно из его свойств, он будет генерировать исключение NullReferenceException.
Пример:
Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.
Вы можно просто избежать этого, проверив, является ли переменная не нулевой:
Person p = null;
if (p!=null)
{
p.Name = "Harry"; // Not going to run to this point
}
Чтобы полностью понять, почему выбрано исключение NullReferenceException, важно знать разницу между типами значений и ссылочные типы .
Итак, если вы имеете дело со типами значений, NullReferenceExceptions не может произойти. Хотя вам нужно поддерживать оповещение при работе со ссылочными типами!
Только ссылочные типы, как следует из названия, могут содержать ссылки или буквально буквально ничто (или «нуль»). Если типы значений всегда содержат значение.
Типы ссылок (эти должны быть проверены):
Типы значений (вы можете просто игнорировать эти):
Это невозможно. Я хотел бы увидеть «статические интерфейсы» , чтобы справиться с этим, но не ожидайте их в ближайшее время ...
Альтернативы:
T
реализует интерфейс) Первые два действительно эквивалентны. В основном вы измените свой прокси-класс на что-то вроде этого:
public class GenericProxy<T>
where T: ClientBase, new()
{
string _configName;
T _proxy;
Func<string, T> _factory;
public GenericProxy(Func<string, T> factory, string configName)
{
_configName = configName;
_factory = factory;
RefreshProxy();
}
void RefreshProxy() // As an example; suppose we need to do this later too
{
_proxy = _factory(_configName);
}
}
(я предполагаю, что вы захотите создать больше экземпляров позже - иначе вы могли бы также передать экземпляр T в конструктор.)
К сожалению, что вы пытаетесь сделать, невозможно.
Как отмечает Джон, для этого нет встроенной поддержки, но в качестве альтернативы вы можете создать типизированный делегат для конструктора (быстрее, чем отражение), используя Expression
. Код для этого можно найти в MiscUtil (в MiscUtil.Linq.Extensions.TypeExt
).
Вот полный рабочий пример, основанный на ответе @JonSkeet:
using System;
using System.Collections.Generic;
namespace GenericProxy
{
class Program
{
static void Main()
{
GenericProxy<ClientBase> proxy = new GenericProxy<ClientBase>(ClientBase.Factory, "cream");
Console.WriteLine(proxy.Proxy.ConfigName); // test to see it working
}
}
public class ClientBase
{
static public ClientBase Factory(string configName)
{
return new ClientBase(configName);
}
// default constructor as required by new() constraint
public ClientBase() { }
// constructor that takes arguments
public ClientBase(string configName) { _configName = configName; }
// simple method to demonstrate working example
public string ConfigName
{
get { return "ice " + _configName; }
}
private string _configName;
}
public class GenericProxy<T>
where T : ClientBase, new()
{
public GenericProxy(Func<string, T> factory, string configName)
{
Proxy = factory(configName);
}
public T Proxy { get; private set; }
}
}
Ожидайте увидеть следующий вывод: ice cream
Это не отвечает на ваш реальный вопрос, ограничивая метод, но для полноты здесь вы можете делать то, что вы спрашиваете во время выполнения, используя отражение:
private T Get<T>(string id)
{
var constructor = typeof(T).GetConstructor(new Type[] { typeof(X), typeof(Y) });
if (constructor == null) throw new InvalidOperationException("The type submitted, " + typeof(T).Name + ", does not support the expected constructor (X, Y).");
var data = GetData(id);
return (T)constructor.Invoke(new object[] { data.x, data.y });
}