Мұндай мінез-құлықты жасайтын неғұрлым генериктер-y жолы бар ма?

Біз DataRow -тен орау және ораудан көп орындаймыз. Иә, біз ORM-ды пайдалануымыз керек, бірақ ол уақытқа дейін бізде бар. Нәтижесінде бұл кодқа ұқсас көптеген код бар:

string username;

var temp = dr["Username"];
if (DbNull.Equals (temp))
{
    username = "Anonymous";
} else {
    username = dr["Username"].ToString();
}

Ақыр соңында, бұл үлгі болды және көмекші әдістерге аударылды:

string username = StringExtensions.SafeParse (dr["Username"], "Anonymous");

This is still cumbersome, and required extension methods for all kinds of primitives. It also clutters up the code. I created a generic extension method, on object, called As. Usage looks like:

string username = dr["Username"].As ("Anonymous");

Бұл салыстырмалы түрде қарапайым өзгеріс басқа әзірлеушілермен өте жақсы кездеседі және көптеген жерлерде қолданылады. Мен риза емеспін - нәтиже ықтимал салдары. Енді мен мерзімінен бұрын оңтайландыруды білмеймін . Мен кодты ешқашан мерзімінен бұрын оңтайландырусыз жазғанмын және оны кейіннен оңтайландыру үлкен мәміле болмауы керек. Мен салыстырмалы түрде қарапайым 2 ГГц жұмыс станциясында секундына екі жарым миллион түрлендіру әдісін салыстырдым және мен бұл басқа дақтарды үнемдейтін уақыт пен салыстырғанда оқуға қабілеттілік күшейіп кеткеніне қарағанда, бұл феноменальды өнімділік екенін мойындау керек. Дегенмен, төмендегі код үлгісін ескере отырып, мен өзімді тілдік ерекшеліктерді дұрыс пайдаланып жатқанды сезінемін және оны әлдеқайда жақсы жасай аламын. Бұл әдіс xmldoc'ed отырып, «HERE BE DRAGONS» дауыстап қатты! Мен қос боксты болдырмаудың жақсы жолын іздеп жүрмін. Қысқарту үшін жібермеген нақты нұсқама, көптеген жағдайларда, TryParse пайдаланады.

public static TDestination As<tdestination> (this object source, TDestination defaultValue = default(TDestination))
{
    if (source is TDestination)
        return (TDestination) source;

    if (source == null || DbNull.Equals(source))
        return defaultValue;

    if (TDestination is int)
        return (TDestination) (object) Convert.ToInt32 (source.ToString ());

    if (TDestination is long)
        return (TDestination) (object) Convert.ToInt64 (source.ToString ());

    if (TDestination is short)
        return (TDestination) (object) Convert.ToInt16 (source.ToString ());

   //and so on...
}
3
As <tdestination> үлгісінің үлгісі компиляцияланбайды.
қосылды автор phoog, көзі

5 жауаптар

Неге объектіңіз IConvertible екенін тексеріңіз және егер ол болса, ToType пайдаланыңыз:

var convertible = source as IConvertible;
if (convertible != null)
    return (TDestination)convertible.ToType(typeof(TDestination), Thread.CurrentThread.CurrentUICulture);
3
қосылды
Маған бұл ұнайды.
қосылды автор Bryan Boettcher, көзі

Сұрағыңызда берілген As әдісінің үлгісіне негізделіп, оны орнына келтіруге болады:

public static TDestination As<tdestination>
    (this object source, TDestination defaultValue = default(TDestination))
{
    if ((source == null) || Convert.IsDBNull(source))
        return defaultValue;

    return (TDestination)source;
}
3
қосылды
@insta: Жоқ, жоқ. Convert.ToInt32 шақырғанға дейін if (source int) әдісін өзіңіздің As әдісімен тексеріңіз және if (source Convert.ToInt64 және т.б. шақырғанға дейін тексеріңіз. source деген int code> ToString және Convert.ToInt32 , одан кейін object /code> - кейін сіз оны TDestination . (Сонымен long , short және т.б.). Сонымен, неге тура жолсыз айналма жолдан аулақ болуға және тікелей TDestination -ге ауысу керек?
қосылды автор LukeH, көзі
Тікелей құйылған бұл кез келген конверсия әрекетін жасайды ма, әлде ол тек жасырын құюға сенеді ме?
қосылды автор Bryan Boettcher, көзі
Мәсіхтің бұл сұрағының себебін түсіндірдім. Менің кодым шын мәнінде түрді (TDestination) тексереді, бұл ойынды сәл өзгертеді.
қосылды автор Bryan Boettcher, көзі

Whenever i goes into reflection or checking the T of my generic class i'm going to use a dictionary Dictionary. As value i always put something in that should be done for each time as Func or Action. In your case i would write it maybe in that way:

public static class MyConverter
{
    private static Dictionary> _MyConverter;

    static MyConverter()
    {
        _MyConverter = new Dictionary>(); //Use the Add() method to include a lambda with the proper signature. _MyConverter.Add(typeof(int), (source) => Convert.ToInt32 (source.ToString())); //Use the index operator to include a lambda with the proper signature. _MyConverter[typeof(double)] = (source) => Convert.ToDouble(source.ToString()); //Use the Add() method to include a more complex lambda using curly braces. _MyConverter.Add(typeof(decimal), (source) => { return Convert.ToDecimal(source.ToString()); }); //Use the index operator to include a function with the proper signature. _MyConverter[typeof(float)] = MySpecialConverterFunctionForFloat; } //A function that does some more complex conversion which is simply unreadable as lambda. private static object MySpecialConverterFunctionForFloat(object source) { var something = source as float?; if (something != null && something.HasValue) { return something.Value; } return 0; } public static TDestination As<tdestination>(this object source, TDestination defaultValue = default(TDestination)) { //Do some parameter checking (if needed). if (source == null) throw new ArgumentNullException("source"); //The fast-path exit. if (source.GetType().IsAssignableFrom(typeof(TDestination))) return (TDestination)source; Func func; //Check if a converter is available. if (_MyConverter.TryGetValue(typeof(TDestination), out func)) { //Call it and return the result. return (TDestination)func(source); } //Nothing found, so return the wished default. return defaultValue; } } 

object пайдалануды қолданатын бұл тәсілдің бірден-бір кемшілігі, функция қысқа уақыт ішінде жиі жиі қайталанса, өнімділік проблемаларын тудыруы мүмкін (un) боксқа әкеледі. Бірақ әрдайым талап ету алдында өлшеп .

Екінші жағынан, одан әрі түрлендіргішті қосу өте оңай, және сөздік пайдалануға байланысты сіз әрқашан O (1) боласыз.

2
қосылды
Бұл керемет көрінеді. Сіз оны тексеру үшін Visual Studio бағдарламасына қатынасыңыз бар ма? Мен конструктормен қиындықтар таптым, өйткені ол TDestination туралы білмейді. Сөздік инициализациясын As әдісіне жылжыту өзін кэштеудің мақсатын жоғалту сияқты көрінеді :(
қосылды автор Bryan Boettcher, көзі

Өріс сияқты бір типтегі өзіңіздің нөлдік мәнін бере алатын өріс өрісі сипаты үшін кеңейтім әдісін анықтау туралы не істеу керек:

        public static T Field(this DataRow row, string columnName, T nullValue) { return !row.IsNull(columnName) ? row.Field(columnName) : nullValue; }
0
қосылды

Field функциясының өзгерісін жасау ең жақсы әдіс болып табылады, бірақ сіз өнімділікке алаңдамасаңыз, онда олар IsNull() немесе нақты өріс функциясын пайдаланбаңыз, себебі олар көп артық тексеруді орындайды. Келесі әдіс сіз шынымен қажет.

public static T Field(this DataRow row, string columnName, T nullValue)
{
  object value = row[columnName];
  return ((DBNull.Value == value) ? nullValue : (T)value);
}

Бұл қосымша бокстың пайда болу қажеттілігін алып тастайды және сіз nullValue параметрін қалай пайдаланатыныңызға мұқият болсаңыз, әдетте функцияны шақырған кезде T анық көрсету керек болады. жеңіске жету.

0
қосылды
Кеңейтім әдiсi DataRow-ге тән емес, бұл жай ғана оны кеңінен қолдану болып табылады. Біз оны жалпы мақсаттағы құю және деректерді түрлендіру әдісі ретінде пайдаланамыз.
қосылды автор Bryan Boettcher, көзі
Бұл жағдайда, бұрын айтылған статикалық конструктор әдісі - ең икемді шешім. Бірақ бұл өте бағаға келеді. Егер сіз өнімділікпен шынымен алаңдасаңыз, сіз өзіңіздің ең жиі кездесетін істеріңіз үшін ұсынғандай AS мамандандырылған функцияларын жасауды қарастырыңыз. Осылайша олар мүмкіндігінше оңтайландырылған болуы мүмкін.
қосылды автор 0rigin, көзі