Мой вопрос касается c # и как получить доступ к статическим членам ... Ну, я действительно не знаю, как это объяснить (что плохо для вопроса, не так ли?) Я просто дам вам пример кода:
Class test<T>{
int method1(Obj Parameter1){
//in here I want to do something which I would explain as
T.TryParse(Parameter1);
//my problem is that it does not work ... I get an error.
//just to explain: if I declare test<int> (with type Integer)
//I want my sample code to call int.TryParse(). If it were String
//it should have been String.TryParse()
}
}
Так что, ребята, спасибо за ваши ответы (кстати, вопрос: как бы я решил эту проблему, не получив ошибку). Это, наверное, довольно простой вопрос для вас!
Спасибо, Никлас
Изменить: Спасибо всем за ваши ответы!
Хотя я думаю, что фраза «поймай и поймай» - самая элегантная, я знаю по своему опыту с vb, что она действительно может быть обломом. Я использовал его один раз, и для запуска программы потребовалось около 30 минут, а позже вычисление заняло всего 2 минуты только потому, что я избегал try-catch.
Вот почему я выбрал swich заявление в качестве лучшего ответа. Это делает код более сложным, но, с другой стороны, я представляю его относительно быстрым и относительно легким для чтения. (Хотя я все еще думаю, что должен быть более элегантный способ ... может быть, на следующем языке, который я изучаю: P)
Хотя, если у вас есть другие предложения, я все еще жду (и готов участвовать)
Еще один способ сделать это, на этот раз отражение в миксе:
static class Parser
{
public static bool TryParse<TType>( string str, out TType x )
{
// Get the type on that TryParse shall be called
Type objType = typeof( TType );
// Enumerate the methods of TType
foreach( MethodInfo mi in objType.GetMethods() )
{
if( mi.Name == "TryParse" )
{
// We found a TryParse method, check for the 2-parameter-signature
ParameterInfo[] pi = mi.GetParameters();
if( pi.Length == 2 ) // Find TryParse( String, TType )
{
// Build a parameter list for the call
object[] paramList = new object[2] { str, default( TType ) };
// Invoke the static method
object ret = objType.InvokeMember( "TryParse", BindingFlags.InvokeMethod, null, null, paramList );
// Get the output value from the parameter list
x = (TType)paramList[1];
return (bool)ret;
}
}
}
// Maybe we should throw an exception here, because we were unable to find the TryParse
// method; this is not just a unable-to-parse error.
x = default( TType );
return false;
}
}
Следующим шагом будет попытка реализовать
public static TRet CallStaticMethod<TRet>( object obj, string methodName, params object[] args );
С полным соответствием типа параметра и т. Д.
Проблема в том, что TryParse не определен ни в интерфейсе, ни в базовом классе, поэтому вы не можете предположить, что тип, переданный в ваш класс, будет иметь эту функцию. Если вы не можете противопоставить Т каким-то образом, вы столкнетесь с этим очень часто.
Вы, вероятно, не можете сделать это.
Прежде всего, если бы это было возможно, вам понадобится более жесткая граница для T, чтобы проверщик типов мог быть уверен, что все возможные замены для T на самом деле имеют статический метод TryParse.
Это не то, как работает статика. Вы должны думать о статиках как о классе Global, даже если они распределены по целому ряду типов. Я рекомендую сделать это свойство внутри экземпляра T, которое может обращаться к необходимому статическому методу.
Также T является фактическим экземпляром чего-либо, и, как и любой другой экземпляр, вы не можете получить доступ к статике для этого типа через экземплярное значение. Вот пример того, что нужно сделать:
class a {
static StaticMethod1 ()
virtual Method1 ()
}
class b : a {
override Method1 () return StaticMethod1()
}
class c : a {
override Method1 () return "XYZ"
}
class generic<T>
where T : a {
void DoSomething () T.Method1()
}
Чтобы получить доступ к члену определенного класса или интерфейса, вам нужно использовать ключевое слово Where и указать интерфейс или базовый класс, который имеет метод.
В приведенном выше примере TryParse не приходит из интерфейса или базового класса, поэтому то, что вы пытаетесь сделать выше, невозможно. Лучше всего использовать Convert.ChangeType и оператор try / catch.
class test<T>
{
T Method(object P)
{
try {
return (T)Convert.ChangeType(P, typeof(T));
} catch(Exception e) {
return null;
}
}
}
Вы можете прочитать мой предыдущий пост об ограничении универсальных типов примитивами . Это может дать вам некоторые указатели в ограничении типа, который может быть передан универсальному типу (поскольку TypeParse , очевидно, доступен только для заданного числа примитивов ( очевидно, исключение является string.TryParse , что не имеет смысла).
Если у вас есть больше дескриптора типа, вы можете попытаться разобрать его. Вам может понадобиться немного уродливый переключатель (чтобы вызвать правильный TryParse ), но я думаю, что вы можете достичь желаемой функциональности.
Если вам нужно, чтобы я объяснил что-либо из вышеперечисленного, пожалуйста, спросите :)
Вы хотите сделать что-то вроде этого:
Class test<T>
{
T method1(object Parameter1){
if( Parameter1 is T )
{
T value = (T) Parameter1;
//do something with value
return value;
}
else
{
//Parameter1 is not a T
return default(T); //or throw exception
}
}
}
К сожалению, вы не можете проверить шаблон TryParse, так как он статический, что, к сожалению, означает, что он не особенно подходит для генериков.
Единственный способ сделать именно то, что вы ищете, это использовать отражение, чтобы проверить, существует ли метод для T.
Другой вариант - убедиться, что отправляемый вами объект является конвертируемым, ограничивая тип IConvertible (все примитивные типы реализуют IConvertible). Это позволит вам очень гибко преобразовать ваш параметр в данный тип.
Class test<T>
{
int method1(IConvertible Parameter1){
IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));
T temp = Parameter1.ToType(typeof(T), provider);
}
}
Вы также можете сделать это, используя тип объекта вместо того, что вы делали изначально.
Class test<T>
{
int method1(object Parameter1){
if(Parameter1 is IConvertible) {
IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));
T temp = Parameter1.ToType(typeof(T), provider);
} else {
// Do something else
}
}
}
Короткий ответ, вы не можете.
Длинный ответ, вы можете обмануть:
public class Example
{
internal static class Support
{
private delegate bool GenericParser<T>(string s, out T o);
private static Dictionary<Type, object> parsers =
MakeStandardParsers();
private static Dictionary<Type, object> MakeStandardParsers()
{
Dictionary<Type, object> d = new Dictionary<Type, object>();
// You need to add an entry for every type you want to cope with.
d[typeof(int)] = new GenericParser<int>(int.TryParse);
d[typeof(long)] = new GenericParser<long>(long.TryParse);
d[typeof(float)] = new GenericParser<float>(float.TryParse);
return d;
}
public static bool TryParse<T>(string s, out T result)
{
return ((GenericParser<T>)parsers[typeof(T)])(s, out result);
}
}
public class Test<T>
{
public static T method1(string s)
{
T value;
bool success = Support.TryParse(s, out value);
return value;
}
}
public static void Main()
{
Console.WriteLine(Test<int>.method1("23"));
Console.WriteLine(Test<float>.method1("23.4"));
Console.WriteLine(Test<long>.method1("99999999999999"));
Console.ReadLine();
}
}
Я сделал статический словарь, содержащий делегат для метода TryParse каждого типа, который я мог бы использовать. Затем я написал общий метод для поиска в словаре и передачи вызова соответствующему делегату. Поскольку каждый делегат имеет свой тип, я просто сохраняю их как ссылки на объекты и возвращаю их к соответствующему универсальному типу, когда получаю их. Обратите внимание, что в качестве простого примера я пропустил проверку ошибок, например, чтобы проверить, есть ли у нас запись в словаре для данного типа.
Хорошо, ребята: Спасибо за всю рыбу. Теперь с вашими ответами и моими исследованиями (особенно в статье об ограничении универсальных типов примитивами ) я представлю вам свое решение.
Class a<T>{
private void checkWetherTypeIsOK()
{
if (T is int || T is float //|| ... any other types you want to be allowed){
return true;
}
else {
throw new exception();
}
}
public static a(){
ccheckWetherTypeIsOK();
}
}
Лучший код: ограничьте T значением ValueType следующим образом:
class test1<T> where T: struct
«Структура» здесь означает тип значения. Строка - это класс, а не тип значения. int, float, Enums - все типы значений.
Кстати, компилятор не принимает вызов статических методов или доступ к статическим членам в «параметрах типа», как в следующем примере, который не будет компилироваться :(
class MyStatic { public static int MyValue=0; }
class Test<T> where T: MyStatic
{
public void TheTest() { T.MyValue++; }
}
=> Ошибка 1 «T» является «параметром типа», который недопустим в данном контексте
Уровень моря
На самом деле это не решение, но в определенных сценариях это может быть хорошей альтернативой: мы можем передать дополнительный делегат универсальному методу.
Чтобы уточнить, что я имею в виду, давайте использовать пример. Допустим, у нас есть некоторый универсальный фабричный метод, который должен создать экземпляр T, и мы хотим, чтобы он затем вызывал другой метод для уведомления или дополнительной инициализации.
Рассмотрим следующий простой класс:
public class Example
{
// ...
public static void PostInitCallback(Example example)
{
// Do something with the object...
}
}
И следующий статический метод:
public static T CreateAndInit<T>() where T : new()
{
var t = new T();
// Some initialization code...
return t;
}
Так что сейчас мы должны сделать:
var example = CreateAndInit<Example>();
Example.PostInitCallback(example);
Тем не менее, мы могли бы изменить наш метод, чтобы получить дополнительный делегат:
public delegate void PostInitCallback<T>(T t);
public static T CreateAndInit<T>(PostInitCallback<T> callback) where T : new()
{
var t = new T();
// Some initialization code...
callback(t);
return t;
}
И теперь мы можем изменить вызов на:
var example = CreateAndInit<Example>(Example.PostInitCallback);
Очевидно, это полезно только в очень специфических сценариях. Но это самое чистое решение в том смысле, что мы получаем безопасность во время компиляции, здесь нет «взлома» и код очень прост.